Задать вопрос
@victor1234
IT: Компьютерное зрение, linux, с++

Как ускорить запрос в postgresql?

Есть таблица с пятью полями типа int. В ней несколько миллионов записей (в дальнейшем будут десятки миллионов или сотни)
Один из столбцов (word_id) проиндексирован b-tree индексом, по нему происходит поиск вида:
select * from keypoints where word_id in ( около 500-1000 значений)

Такой запрос на моем неспешном ноуте занимает 200-300с, iotop показывает, что идет чтение с диска. В результате может вернуться десятки тысяч записей. Повторно запрос выполняется много быстрее.

В конфиге postgresql менял настройки только авторизации.

Вопрос: такой запрос впринципе некорректен и не может выполняться быстро или можно что-то соптимизировать?

Update:
Вывод explain для одного where:
explain select * from keypoints where word_id=27601;

Bitmap Heap Scan on keypoints (cost=28.37..4118.23 rows=1080 width=24)
Recheck Cond: (word_id = 27601)
-> Bitmap Index Scan on keypoints_search_idx (cost=0.00..28.10 rows=1080 width=0)
Index Cond: (word_id = 27601)
(4 rows)

Time: 696,716 ms
  • Вопрос задан
  • 2444 просмотра
Подписаться 5 Оценить Комментировать
Пригласить эксперта
Ответы на вопрос 3
Melkij
@Melkij
PostgreSQL DBA
По порядку:
Повторно запрос выполняется много быстрее.

Значит, вы с головой упёрлись в диск.
Вариант есть только один - ставить быстрый диск.
Вариант похуже, т.к. диск всё равно у вас останется медленный и если не на чтении, то на старте и на записи вы туда упрётесь очень хорошо - использовать больше памяти. Чтобы как можно данных было в памяти. Но при работе эти данные всё равно надо поднимать с медленных дисков, потому базу сначала придётся разогревать. Есть даже штатная утилитка pg_prewarm

В конфиге postgresql менял настройки только авторизации.

По дефолту там что-то несмешное вместо shared_buffers. 32 что ли мегабайта? И это на ключевую характеристику базы.
Увеличивать хотя бы до 20% от общего объёма памяти на машине.

В третьих, актуальный Postgresql довольно плохо умеет развесистый IN. И тому есть не совсем очевидное решение - переписать в join:
select count(*) from test JOIN (VALUES (1),...,(10000)) AS v(val) USING (val);

Такой вот hash join работает быстрее IN. На 10000 элементах получали 10мс против 380мс у IN.
Ответ написан
Комментировать
MaxDukov
@MaxDukov
впишусь в проект как SRE/DevOps.
думаю, пора открывать для себя команду EXPLAIN и посмотреть план исполнения, используются ли индексы вообще. Причем смотрите как на основной запрос, так и на вложенный.
судя по
занимает 200-300с, iotop показывает, что идет чтение с диска.
- нет, происходит FULL SCAN.
правка конфига поможет, если захотите поиграться с размерами кэша. Пока проблема, похоже, именно в индексах.
Ответ написан
@dmitryKovalskiy
программист средней руки
А как определяется список "около 500-1000 значений"? Я бы постарался избавиться от вложенного запроса. Через Join или некое WHERE.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы