Подтвердить что ты не робот

Улучшение скорости запросов: простой SELECT в большой таблице postgres

У меня возникают проблемы с скоростью в запросе SELECT в базе данных Postgres.

У меня есть таблица с двумя целыми столбцами в качестве ключа: (int1, int2) Эта таблица содержит около 70 миллионов строк.

Мне нужно сделать два простых запроса SELECT в этой среде:

SELECT * FROM table WHERE int1=X;
SELECT * FROM table WHERE int2=X;

Эти два выбора возвращают около 10.000 строк каждый из этих 70 миллионов. Чтобы это работало как можно быстрее, я подумал об использовании двух индексов HASH, по одному для каждого столбца. К сожалению, результаты не так хороши:

                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on lec_sim  (cost=232.21..25054.38 rows=6565 width=36) (actual time=14.759..23339.545 rows=7871 loops=1)
   Recheck Cond: (lec2_id = 11782)
   ->  Bitmap Index Scan on lec_sim_lec2_hash_ind  (cost=0.00..230.56 rows=6565 width=0) (actual time=13.495..13.495 rows=7871 loops=1)
         Index Cond: (lec2_id = 11782)
 Total runtime: 23342.534 ms
(5 rows)

Это пример EXPLAIN ANALYZE одного из этих запросов. Это занимает около 23 секунд. Мои ожидания - получить эту информацию менее чем за секунду.

Это некоторые параметры конфигурации postgres db:

work_mem = 128MB
shared_buffers = 2GB
maintenance_work_mem = 512MB
fsync = off
synchronous_commit = off
effective_cache_size = 4GB

Любая помощь, комментарий или мысль будут действительно оценены.

Спасибо заранее.

4b9b3361

Ответ 1

Извлечение моих комментариев в ответ: поиск индекса здесь был очень быстрым - все время было потрачено на извлечение фактических строк. 23 секунды /7871 строк = 2,9 миллисекунды на строку, что разумно для получения данных, которые разбросаны по дисковой подсистеме. Иски медленны; вы можете a) поместить свой набор данных в ОЗУ, b) купить SSD или c) организовать свои данные заблаговременно, чтобы свести к минимуму запросы.

PostgreSQL 9.2 имеет функцию, называемую сканирование только по индексу, которая позволяет ему (обычно) отвечать на запросы без доступа к таблице. Вы можете комбинировать это с свойством индекса btree для автоматического поддержания порядка, чтобы быстро выполнить этот запрос. Вы указываете int1, int2 и два поплавка:

CREATE INDEX sometable_int1_floats_key ON sometable (int1, float1, float2);
CREATE INDEX sometable_int2_floats_key ON sometable (int2, float1, float2);

SELECT float1,float2 FROM sometable WHERE int1=<value>; -- uses int1 index
SELECT float1,float2 FROM sometable WHERE int2=<value>; -- uses int2 index

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

Ответ 2

Спасибо, ты будешь. Как вы заметили, проблема заключалась в поиске через HD и не поиске индексов. Вы предложили множество решений, таких как загрузка набора данных в оперативной памяти или покупка HD-SSD. Но, забыв об этих двух, которые связаны с управлением вещами вне самой базы данных, вы предложили две идеи:

  • Реорганизовать данные, чтобы уменьшить поиск данных.
  • Использовать функцию PostgreSQL 9.2 "только для индексирования"

Поскольку я находится на сервере PostgreSQL 9.1, я решил использовать опцию "1".

Я сделал копию таблицы. Итак, теперь у меня одна и та же таблица с теми же данными дважды. Я создал индекс для каждого из них, первый из которых индексируется (int1), а второй - (int2). Затем я сгруппировал их (таблица CLUSTER USING ind_intX) с помощью соответствующих индексов.

Теперь я отправляю EXPLAIN ANALYZE того же запроса, который выполняется в одной из этих кластерных таблиц:

                                                         QUERY PLAN                                                            
---------------------------------------------------------------------------------------------------------------------------------------------  
Index Scan using lec_sim_lec2id_ind on lec_sim_lec2id  (cost=0.00..21626.82 rows=6604 width=36) (actual time=0.051..1.500 rows=8119 loops=1)
Index Cond: (lec2_id = 12300)  Total runtime:
1.822 ms (3 rows)

Теперь поиск действительно быстрый. Я снизился с 23 секунд до ~ 2 миллисекунд, что является впечатляющим улучшением. Я думаю, что эта проблема решена для меня, я надеюсь, что это может быть полезно и для других, испытывающих ту же проблему.

Большое вам спасибо.

Ответ 3

У меня был случай сверх медленных запросов, в которых между одним и несколькими соединениями (в PG v9.1) выполнялась таблица, которая составляла 33 миллиона строк для дочерней таблицы, размер которой составлял 2,4 миллиарда строк. Я выполнил CLUSTER по индексу внешнего ключа для дочерней таблицы, но обнаружил, что это не решило мою проблему с таймаутами запросов даже для самых простых запросов. Запуск ANALYZE также не помог решить проблему.

Что имело огромное значение, так это выполнение руководства VACUUM как для родительской таблицы, так и для дочерней таблицы. Даже когда родительская таблица завершала процесс VACUUM, я перешел с 10-минутных тайм-аутов на результаты, возвращающиеся через одну секунду.

То, что я убираю из этого, состоит в том, что регулярные операции VACUUM по-прежнему имеют решающее значение, даже для v9.1. Причина, по которой я делал это, заключалась в том, что я заметил, что autovacuum не работал ни на одном из столов в течение как минимум двух недель, и с тех пор произошло много перегрузок и вставок. Возможно, мне нужно улучшить автовакуумный триггер, чтобы позаботиться об этом вопросе, но я могу сказать, что таблица 640 ГБ с несколькими миллиардами строк работает хорошо, если все очищено. Мне еще не пришлось разбивать таблицу, чтобы получить хорошую производительность.