DEV Community

Cover image for "Тайны Pawsgresville: Дело об исчезнувших данных". Идексы Postgres.
Olga Lugacheva
Olga Lugacheva

Posted on

"Тайны Pawsgresville: Дело об исчезнувших данных". Идексы Postgres.

В оживлённом городе Pawsgresville, где данные текли, как реки, и порой так же непредсказуемо исчезали, работал частный сыщик по имени Биллометр Би-Tри, или просто B-Tree. Его офис, утопающий в бумагах и запахе дешёвого кофе, всегда был полон клиентов, жаждущих разгадать загадки своих запросов: найти потерянные данные, упорядочить списки или вычислить хитроумные диапазоны. Но однажды на пороге его офиса появилась проблема, способная перевернуть весь порядок в городе.

lika

Глава 1. Таинственное исчезновение

В тот день в офис вошла нервная сиамская кошка с лэптопом в лапах. Её звали Лайка, и она владела небольшим архивом запросов. Её проблема была сложной: «Мой поиск перестал работать. Я ввожу ‘%tractor%’, а результаты находят всё, что угодно, кроме нужного!»

B-Tree прищурился и задумчиво посмотрел на экран. «Так-так. LIKE с ведущим процентом, говорите? Вы использовали индекс?»

«Конечно! У меня стоит обычный B-Tree индекс, но он будто игнорируется!»

«Вот в чём дело, мадам, — сказал детектив, — ваш запрос с ведущим ‘%’ вынуждает систему сканировать всю таблицу. Индексы B-Tree просто не предназначены для таких трюков. Но не волнуйтесь, у меня есть решение. Это работа для профессора GIN.»


Глава 2. GIN и загадка словаря

В соседнем районе, среди пыльных книжных полок, жил профессор GIN. Он знал всё о полнотекстовом поиске и индексах, которые могли справиться с задачами, перед которыми B-Tree пасовал.

«Профессор GIN, нам нужна ваша помощь!» — заявил B-Tree, ввалившись в библиотеку. Лайка, прижав уши, следовала за ним.

«Что у вас на этот раз?» — спросил GIN, глядя поверх очков.

«LIKE с ведущим процентом. И обычный B-Tree не справляется.»

Профессор склонился над лэптопом, перебирая данные. «Вам нужен GIN-индекс или хотя бы trigram extension. Смотрите, — он открыл словарь на слове ‘трактор’ и показал, как индекс можно использовать для разбиения слов на подстроки. — GIN отлично работает, если вы часто ищете фрагменты слов. Но помните: если данных слишком мало, накладные расходы на создание такого индекса могут быть не оправданы.»

CREATE INDEX idx_content ON documents USING gin(to_tsvector('english', content));
SELECT * FROM documents WHERE to_tsvector('english', content) @@ to_tsquery('tractor');

Enter fullscreen mode Exit fullscreen mode

Лайка выглядела впечатлённой. «Такой индекс точно решит мою проблему?»

«В большинстве случаев, да. Но есть нюанс: Если вам придётся часто добавлять или обновлять данные, мой индекс может замедлить операции. Это моя слабость. А для сложных пространственных задач есть ещё один эксперт.»


Глава 3. Заговор полигона

gist
В другой части города произошла кража. Билл и GIN направились к старому знакомому — мистеру GIST. Этот рыжий кот, известный своим свободным характером, обитал в мастерской на окраине города, где возился с геометрическими запросами и сложными структурами данных. Карта геометрических данных оказалась повреждена.
«Я слышал, вам нужны индексы для хитрых диапазонов?» — спросил GIST, подкручивая свои усы.
«Именно так. Кто-то украл часть данных о полигонах, — сказал Билл. — Нам нужно найти все области, которые пересекаются с этим прямоугольником!Нам нужны индексы, которые могут обрабатывать запросы вроде ‘найти все записи внутри определённого квадрата или диапазона значений.’»

GiST кивнул и создал геометрический индекс:

CREATE INDEX idx_map ON locations USING gist(location);
SELECT * FROM locations WHERE location && 'BOX(1 1, 5 5)'::box;

Enter fullscreen mode Exit fullscreen mode

— Ваш случай особый, — добавил GiST. — Мне хорошо даются сложные данные, но для простых диапазонов, таких как даты, я могу быть медленнее.


Глава 4. BRIN и огромные архивы

brin
Тем временем в архиве городского регистратора застряли поисковики. База данных была огромной, и никто не мог найти документы за определённый месяц.
— У нас есть BRIN! — предложил архивариус. — Он отлично справляется с большими данными, которые хранятся в упорядоченном виде.

BRIN подошёл к задаче с минималистичным подходом:

CREATE INDEX idx_events ON events USING brin(event_date);
SELECT * FROM events WHERE event_date BETWEEN '2024-01-01' AND '2024-01-31';

Enter fullscreen mode Exit fullscreen mode

— Я экономлю много места, — объяснил BRIN, — но если данные не упорядочены, моя эффективность падает.


Глава 5. Загадка логических связок

Тем временем детектив Биллометр Би-Три столкнулся с новой задачей. Клиент попросил найти данные с двумя условиями:

SELECT * FROM users WHERE age = 25 AND city = 'Saint-Pawsburg';
Enter fullscreen mode Exit fullscreen mode

— Ну что ж, детективы, за дело! — сказал Билл.

B-Tree любит, чтобы всё было по порядку. Для этого в таблице был индекс:

CREATE INDEX idx_users_age_city ON users (age, city);
Enter fullscreen mode Exit fullscreen mode

— Отлично! — сказал B-Tree. — Я знаю, как искать: сначала найду всех, у кого age = 25, а потом проверю city = 'Saint-Pawsburg'.

Но тут прилетел другой запрос:

SELECT * FROM users WHERE city = 'Saint-Pawsburg' AND age = 25;
Enter fullscreen mode Exit fullscreen mode

Билл вздохнул:
— Порядок в индексе важен! У меня сначала age, а потом city. Если ты начнёшь с city, я не смогу сразу сузить круг поиска.
Клиент ушел, но вернулся с новым запросом
— Мне нужны записи, где возраст = 25 ИЛИ город = 'Saint-Pawsburg'.

Билл задумался. Он знал, что его индекс поддерживает запросы с AND, но с OR всё сложнее. Если у тебя есть индексы только на возраст и на город, то Postgres объединяет результаты:

SELECT * FROM users WHERE age = 25 OR city = 'Saint-Pawsburg';
Enter fullscreen mode Exit fullscreen mode

Но это может быть медленно. OR-запросы редко работают хорошо с одним индексом. Придётся работать тяжело: я сначала найду всех с age = 25, а потом добавлю тех, у кого city = 'Saint-Pawsburg'.
— Лучший способ ускорить такие запросы — отдельные на каждую колонку индексы
CREATE INDEX idx_age ON users (age);
CREATE INDEX idx_city ON users (city);
или пересмотр условий, — посоветовал Билл.

На следующий день Билл отправился в Зал Планировщиков.


Глава 6. Планировщик и великий анализ

Это было место, где великий Query Planner обучал новичков искусству составления планов выполнения запросов. Он стоял у доски, показывая разницу между Index Scan и Sequential Scan, а детектив сидел в заднем ряду, внимательно слушая. Планировщик решал, использовать ли индекс вообще.

— Ваш запрос, — сказал он Биллу, — слишком неэффективен. Данные малы, поэтому последовательный обход таблицы будет быстрее.

Поняв это, Билл стал чаще советовать клиентам использовать EXPLAIN ANALYZE, чтобы понять, как Postgres выбирает планы:

EXPLAIN ANALYZE SELECT * FROM users WHERE age = 25;

Enter fullscreen mode Exit fullscreen mode

Это помогало увидеть, использует ли запрос индекс и почему.

«Планировщик — тот ещё хитрец, — говорил Query Planner, — он выбирает путь, который считает оптимальным, но не всегда угадывает. Индекс может не использоваться, если таблица маленькая или если фильтр слишком общий. А ещё есть VACUUM. Без регулярной уборки таблицы индексы могут терять свою эффективность.»
Билл кивнул. Он знал, что VACUUM — это тот самый процесс, который убирает мёртвые строки и держит данные в форме. «Значит, уборка так же важна, как и хороший план.»

pers


Глава 7: Секреты подземелий VACUUM

Когда детектив B-Tree думал, что дело близится к завершению, в офис постучался новый клиент. Это был пушистый белый кот по имени Vacuum, известный в Пушгресвиле как мастер уборки данных.

— «Детектив B-Tree, я заметил странности в подземных хранилищах данных. Кажется, там накопилось слишком много мусора, и я не могу эффективно выполнять свою работу!»

B-Tree приподнял шляпу, задумавшись:

— «Расскажите подробнее, Vacuum. Вы ведь тот самый, кто заботится о чистоте таблиц и удалении устаревших строк?»

Vacuum кивнул.

— «Верно. Но в последнее время в таблицах Пушгресвиля полно мёртвых строк, оставшихся после DELETE и UPDATE запросов. Их никто не убирает, и это мешает индексам работать быстро. А ещё... кажется, некоторым индексам нужна моя помощь, чтобы оставаться компактными.»

— «Компактность... это звучит серьёзно,» — пробормотал B-Tree.

Vacuum разложил карту:

Обычный VACUUM: он очищает мёртвые строки, но не возвращает место операционной системе.
VACUUM FULL: он полностью реорганизует таблицы, возвращая свободное пространство системе. Правда, это требует блокировки таблиц.
Автовакуум: работает в фоновом режиме, но иногда не справляется, если нагрузка слишком велика или настройки сервера не оптимальны.
— «Это как убирать мусор с улиц. Если делать это нерегулярно, движение по городу замедляется,» — объяснил Vacuum.

B-Tree вспомнил, как недавно планировщик запросов жаловался на Sequential Scan, который с каждым днём становился всё медленнее.

— «Значит, ты не только убираешь мусор, но и помогаешь индексам оставаться эффективными?»

Vacuum улыбнулся.

— «Точно! Без меня GIN и GiST начинают страдать, ведь их размеры увеличиваются, а поиск становится медленнее. А BRIN вообще может потерять свой смысл, если я не буду поддерживать порядок в данных.»

vac

Полезные советы для расследований

  • Индексы работают быстрее, если запросы учитывают порядок столбцов. Например, если индекс CREATE INDEX idx_users_age_city ON users (age, city), то запросы вроде WHERE age = ... AND city = ... будут работать эффективно. А вот WHERE city = ... AND age = ... могут быть медленнее.
  • Сложные условия с OR редко эффективно используют индексы.
  • Если много OR-условий, попробуйте переписать запросы или создать несколько индексов.
  • Для LIKE лучше использовать pg_trgm.
    Подключите расширение:
    CREATE EXTENSION pg_trgm;
    CREATE INDEX idx_users_name_trgm ON users USING gin (name gin_trgm_ops);

    Это ускорит запросы вроде LIKE '%подстрока%'.

  • Проверяйте запросы с помощью EXPLAIN ANALYZE. Оно покажет, что делает планировщик, и подскажет, как улучшить запрос.

  • Не злоупотребляйте индексами. Они ускоряют чтение, но замедляют вставку и обновление данных.

Когда использовать какой индекс?

Каждый тип индекса в PostgreSQL — мастер в своём деле:
B-Tree — универсальный детектив. Поиск равенства и диапазонов, сортировка.
GIN — быстрый эксперт по множественным совпадениям. Полнотекстовый поиск, JSONB, массивы.
GiST — аналитик сложных структур. Геометрия, диапазоны, сложные типы данных.
BRIN — экономный, но эффективный на огромных данных. Очень большие таблицы, упорядоченные данные.


Так в Клубе Индексов каждый запрос находил своего детектива. А Pawsgresvill оставался самым быстрым и умным городом баз данных, потому что его жители всегда соблюдали порядок в индексе и использовали подходящие инструменты. 🕵️‍♂️

Top comments (0)