lesson ── postgres-labs ── ~22 мин ── 4 шагов
B-tree бессилен против поиска подстроки LIKE '%x%' и полнотекста.
Увидим это, потом построим GIN по tsvector и сравним. Запусти psql
во вкладке client.
интерактивный sandbox
Поднимется контейнер postgreslab/postgres-base с PostgreSQL 17 и psql. В браузере откроется терминал, база lab уже настроена. Каждый шаг проверяется автоматически. Сеть air-gapped, наружу контейнер не ходит.
stack ── PostgreSQL 17 · psql · 1 GB RAM · air-gapped · самоуничтожается через 45 мин простоя
CREATE TABLE docs AS
SELECT g id,
'рейс ' || g || ' статус ' ||
(CASE WHEN g % 3 = 0 THEN 'задержка' ELSE 'вовремя' END) AS body
FROM generate_series(1, 200000) g;
ANALYZE docs;
У каждого документа несколько слов - это случай для инвертированного индекса.
✓ Таблица текстов готова.
CREATE INDEX docs_btree ON docs (body);
EXPLAIN SELECT * FROM docs WHERE body LIKE '%задержка%';
Подстрока в середине - начало неизвестно, B-tree не строит диапазон. План Seq Scan. Предскажи это до запуска.
LIKE 'abc%' (префикс) btree берёт, '%abc%' (подстрока) - нет.
✓ Seq Scan - btree бессилен против подстроки.
CREATE INDEX docs_fts ON docs USING gin (to_tsvector('simple', body));ANALYZE docs;
GIN строит инвертированный индекс: слово → список документов.
to_tsvector разбирает текст на слова, GIN индексирует каждое.
✓ GIN-индекс по tsvector построен.
EXPLAIN SELECT * FROM docs
WHERE to_tsvector('simple', body) @@ to_tsquery('simple', 'задержка');Тот же оператор-выражение, что в индексе - план идёт по GIN (Bitmap Index Scan). Сравни время с LIKE через EXPLAIN ANALYZE.
Выражение в запросе должно совпасть с выражением индекса.
✓ Полнотекст ушёл в GIN - быстро вместо Seq Scan.
B-tree не ускоряет LIKE '%x%' (подстрока без известного начала) и полнотекст. GIN - инвертированный индекс «элемент → список строк» - решает это: по tsvector с оператором @@ поиск по словам идёт через индекс. GIN быстр на чтение и дорог на запись (pending list, fastupdate).
команды
CREATE INDEX ON t USING gin (to_tsvector('simple', body));GIN для полнотекстаWHERE to_tsvector('simple', body) @@ to_tsquery('simple','x')поиск по словамконцепции