Какова будет размер базы данных, если в ней будет 10 000 строк и общий объем данных составит 500 000 000?

У вас есть следующая структура базы данных: <br/> <pre><code class="sql">CREATE TABLE base
(
    id       BIGSERIAL PRIMARY KEY,
    url      text NOT NULL,
    login    text NOT NULL,
    name text NOT NULL
);</code></pre> <br/> <br/> Каковы способы ускорения поиска по полям url, login и name? <br/> При этом необходимо отметить, что некоторые индексы не могут быть применены из-за превышения предельного размера строки индекса (3432) для btree версии 4, который составляет 2704 (например, индекс "idx_base_url") (SQLSTATE 54000). <br/> <br/> В базе данных есть записи в столбце url, содержащие слово login, такие как "cloudflare.com/login" и "login.test.com". Мне необходимо реализовать поиск по таким записям, при этом результат должен содержать не более 10,000 строк и быть получен за 10 секунд. <br/> <br/> P.S.: На бэкенде используется Golang. <br/> P.S.2: Кеширование данных не является вариантом, так как после поиска строки удаляются.
  • 29 августа 2024 г. 14:55
Ответы на вопрос 7
Чтобы определить размер базы данных и разобраться с методами ускорения поиска по полям `url`, `login` и `name`, необходимо учитывать несколько факторов.

### 1. Размер базы данных

Если у вас есть 10 000 строк и общий объем данных составляет 500 000 000 байт (или 500 МБ), то средний размер строки можно вычислить следующим образом:

- Общий объем данных: 500,000,000 байт
- Количество строк: 10,000

Средний размер строки = 500,000,000 / 10,000 = 50,000 байт (или ~50 КБ).

Это достаточно большой средний размер строки, учитывая, что в таблице несколько текстовых полей. Рекомендуется проверять конкретное содержимое полей, чтобы оценить, не содержит ли таблица пустых или сильно нагруженных данных, так как это может повлиять на производительность.

### 2. Методы ускорения поиска

Поскольку вы имеете дело с текстовыми полями и возможны проблемы с размером индекса, вот несколько способов ускорения поиска:

1. **Индексы с использованием пэтчей**:
   - Если строки в поле `url` могут превышать размер индекса, рекомендуется рассмотреть использование префиксных индексов или триггеров, позволяющих обрезать строки до допустимого размера. Например, создание индекса на первые 255 символов:
     ```sql
     CREATE INDEX idx_base_url_prefix ON base (url varchar(255));
     CREATE INDEX idx_base_login_prefix ON base (login varchar(255));
     CREATE INDEX idx_base_name_prefix ON base (name varchar(255));
     ```

2. **Полнотекстовый поиск**:
   - Использование встроенного механизма полнотекстового поиска PostgreSQL. Можно создать `GIN`-индекс на поле `url` с типом данных `tsvector`:
     ```sql
     ALTER TABLE base ADD COLUMN url_tsv tsvector;
     UPDATE base SET url_tsv = to_tsvector(url);
     CREATE INDEX idx_base_url_tsv ON base USING GIN(url_tsv);
     ```
   - Для поиска:
     ```sql
     SELECT * FROM base WHERE url_tsv @@ to_tsquery('login');
     ```

3. **Партиционирование таблиц**:
   - Если размер данных продолжает расти, рассмотрите возможность партиционирования таблицы по некоторым критериям (дата создания записи, тип пользователя и т.д.), чтобы облегчить обработку подзапросов и объединений.

4. **Оптимизация структуры запросов**:
   - При написании SQL-запросов используйте `EXPLAIN` для анализа плана выполнения запросов и находите узкие места в производительности.

5. **Избегайте `SELECT *`**:
   - Вместо этого выбирайте только необходимые поля в запросах. Это уменьшает количество передаваемых данных и может ускорить выполнение.

6. **Наиболее подходящий алгоритм поиска**:
   - Учитывайте возможности, такие как `ILIKE`, чтобы сделать поиск нечувствительным к регистру, если это требуется, хотя это может потребовать дополнительных индексов.

### 3. Условия выполнения поиска

Что касается ограничения в 10,000 строк и времени исполнения не более 10 секунд:

- Убедитесь, что индексы правильно настроены и обновляются.
- Оцените нагрузку на базу данных, используя инструменты мониторинга PostgreSQL, например, `pg_stat_statements`.
- На стороне Golang оптимизируйте время выполнения, используя асинхронные запросы или системы очередей, если следует ожидать блокировок.

Для выполнения всех этих рекомендаций, вам нужно тестировать и наблюдать за поведением базы данных под нагрузкой, вносить необходимые изменения и продолжать оптимизацию, исходя из конкретных потребностей вашей системы.
Запрос
SELECT * FROM base WHERE domain LIKE '%habr%' LIMIT 10000;


Какой это нахрен поиск "по совпадениям"??? Это, блин, поиск по подстроке!!! причём в любом месте строки.

С таким шаблоном поиска можете с обычными индексами вообще не трахаться, по причине абсолютной бессмысленности действа. Читайте про GIN, GiST, RUM и прочие виды индексов и поиск с их использованием.
Первое, что приходит в голову - разделить домен и URI. Тогда можно будет сначала поискать по индексу доменов, а потом уже эту выборку по урлу.
индекс по like %login% не будет применяться. нужно добавить колонку типа булево и partial index на неё. При вставке/обновлении и один раз при создании колонки - заполнить её значением true для нужных строк.
Избавляйся от like '%...%', это худший способ искать, он индексы не использует 
даже поиск с регулярными выражениями работает лучше (правда там лимит на размер строки)

Если like используется чтобы искать части url, то храни в базе не целиком а разделенную по полям, в идеале чтобы поиск был на равенство (даже если база увеличится и станет сложнее, например тебе нужно искать по параметрам GET url, т.е. потребуется создать еще одну таблицу на них, скорость все равно будет хорошей)
А мне первое что приходит на ум, реализовать собственный словарик + табличку в которой будут указаны сопоставления ID слов из словарика и ID из таблицы с текстом в которых встречается данное слово. 
Я так делал, когда на проекте поиск был по большим кускам текста, который был в BLOB-ах. Я разделял текст на слова по пробелам.
Использовали этот поиск редко, но когда запускали, он висел минут 10-20, люди уходили пить чай наверно :) и всех это устраивало. Но однажды этот поиск повесил БД, и эта проблема оказалась в поле моего зрения. После формирования словарика и добавления функций чтобы по новым записям так же добавлялась инфа в словарик, поиск стал занимать доли секунды :)
SELECT * FROM base WHERE domain LIKE '%habr%' LIMIT 10000;


проще всего так, наверно:

CREATE EXTENSION pg_trgm;

CREATE INDEX trgm_domain_base_idx ON base USING GIST (domain gist_trgm_ops);

EXPLAIN ANALYZE SELECT * FROM base WHERE domain LIKE '%habr%' LIMIT 10000;


https://www.postgresql.org/docs/current/pgtrgm.html
Похожие вопросы