1

Это вопрос к вопросу на Slow Postgres 9.3 queries.Slow Postgres 9.3 Запросов, опять

Новые индексы определенно помогают. Но то, что мы видим, иногда запросы на практике гораздо медленнее, чем при запуске EXPLAIN ANALYZE. Примером может служить следующее, бегите на производственной базе данных:

explain analyze SELECT * FROM messages WHERE groupid=957 ORDER BY id DESC LIMIT 20 OFFSET 31980; 
                     QUERY PLAN 
--------------------------------------------------------------------------------------------------------------------------------------------------------- 
Limit (cost=127361.90..127441.55 rows=20 width=747) (actual time=152.036..152.143 rows=20 loops=1) 
    -> Index Scan Backward using idx_groupid_id on messages (cost=0.43..158780.12 rows=39869 width=747) (actual time=0.080..150.484 rows=32000 loops=1) 
     Index Cond: (groupid = 957) 
Total runtime: 152.186 ms 
(4 rows) 

При медленном протоколирования запросов включена, мы видим примеры этого запроса с более 2-х секунд. У нас также есть log_lock_waits=true, и медленные блокировки не сообщаются примерно в одно и то же время. Что может объяснить огромную разницу во времени исполнения?

+0

Когда вы объясняете анализ, вы, вероятно, получили данные «горячие» в кеше. В этих случаях вы, вероятно, этого не делаете, поэтому больше ввода-вывода. Используйте 'explain (buffers, analysis)', чтобы увидеть использование буфера. –

+0

Для чего вам нужен офсет? Если для разбивки на страницы читайте [this] (http://use-the-index-luke.com/no-offset). Возможно, вы можете ускорить обработку, делая вещи по-другому. –

ответ

3

LIMIT x OFFSET y обычно выполняет не намного быстрее, чем LIMIT x + y. Большой OFFSETвсегда сравнительно дорогой. Предлагаемый индекс in the linked question помогает, но пока вы не можете получить только для индексации, Postgres все же должен проверить видимость в куче (основное отношение) не менее x + y строк, чтобы определить правильный результат.

SELECT * 
FROM messages 
WHERE groupid = 957 
ORDER BY id DESC 
LIMIT 20 
OFFSET 31980; 

CLUSTER на индексе (groupid,id) будет способствовать увеличению локальности данных в куче и уменьшить количество страниц данных для чтения на запрос. Определенно победа. Но если все groupid будут в равной степени опрошены, это не удалит узкое место слишком маленького ОЗУ для кеша. Если у вас есть параллельный доступ, рассмотрим pg_repack вместо CLUSTER:

ли вы на самом деле нужно все столбцы вернулись? (SELECT *) Индекс покрытия, разрешающий Сканирование только по индексу может помочь, если вам потребуется только несколько небольших столбцов. (autovacuum должен быть достаточно сильным, чтобы справляться с записью в таблицу).

Кроме того, согласно вашему связанному вопросу, ваша таблица составляет 32 ГБ на диске. (Обычно это немного больше в ОЗУ). Индекс (groupid,id) добавляет еще 308 MB по крайней мере (без наворотов):

SELECT pg_size_pretty(7337880.0 * 44); -- row count * tuple size 

У вас есть 8 Гб оперативной памяти, из которых вы ожидаете около 4,5 Гб, которые будут использоваться для кеша (effective_cache_size = 4608MB). Этого достаточно для кэширования индекса для повторного использования, но недостаточно для кэширования всей таблицы.

Если ваш запрос происходит, чтобы найти страницы данных в кеше, это быстро. Иначе, не так много. Большая разница, даже с SSD-хранилищем (гораздо больше с HDD).

Не имеет отношения к этому запросу, но 8 МБ от work_ mem (work_mem = 7864kB) кажется способным к малым для вашей установки. В зависимости от других факторов я бы установил это как минимум 64 МБ (если у вас не много одновременных запросов с сортировкой/хэш-операциями). Как @Craig прокомментировал, EXPLAIN (BUFFERS, ANALYZE) может рассказать нам больше.

Наилучший план запроса также зависит от значений частот. Если фильтр пропускает всего несколько строк, результат может быть пустым для определенного groupid, и запрос выполняется сравнительно быстро. Если требуется извлечь большую часть таблицы, выигрывает обычное последовательное сканирование. Вам нужна действительная статистика таблицы (autovacuum). И, возможно, больше статистики мишенью для groupid:

+0

О, и Postgres 9.3 стареет. Модернизация до 9.6 может помочь. Среди многих других были различные улучшения производительности с большими таблицами. –

+1

Небольшое примечание к PG 9.6 - Я нашел, что pg 9.6.1 требует более высоких настроек work_mem на той же HW, что и ранее, на стр. 9.5. Я нашел это, потому что затраты на некоторые запросы, очень хорошо работающие на 9,5, внезапно были абсолютно ужасны на 9.6 – JosMac

1

Поскольку OFFSET медленно, альтернатива для имитации OFFSET с использованием другого столбца и некоторой подготовки индекса. Нам нужен столбец UNIQUE (как ПЕРВИЧНЫЙ КЛЮЧ) на столе. Если его нет, один может быть добавлен с:

CREATE SEQUENCE messages_pkey_seq ; 
ALTER TABLE messages 
    ADD COLUMN message_id integer DEFAULT nextval('messages_pkey_seq'); 

Далее мы создаем position колонку для OFFSET моделирования:

ALTER TABLE messages ADD COLUMN position INTEGER; 
UPDATE messages SET position = q.position FROM (SELECT message_id, 
    row_number() OVER (PARTITION BY group_id ORDER BY id DESC) AS position 
    FROM messages) AS q WHERE q.message_id=messages.message_id ; 
CREATE INDEX ON messages (group_id, position) ; 

Теперь мы готовы к новой версии запроса в OP :

SELECT * FROM messages WHERE group_id = 957 AND 
    position BETWEEN 31980 AND (31980+20-1) ; 
+0

В нижней части этот метод хорош только для таблиц только для чтения, или столбец 'position' должен быть пересчитан после изменений в работе правильно –

 Смежные вопросы

  • Нет связанных вопросов^_^