LINUX.ORG.RU

Postgres, пагинация и длинные запросы

 ,


0

4

Ситуация такая: есть постгрес с огромной и медленной базой на тормознутом сервере.

Сейчас каждый запрос — это SELECT COUNT(*) FROM QUERY для оформления кнопочек пагинации, и SELECT * FROM QUERY LIMIT X OFFSET Y собственно для выбора каждой отдельной страницы.

Можно ли как-нибудь совместить эти два запроса в один? Чтобы я одним запросом получил и общее количество данных, и нужную мне страницу?

Сейчас я запилил кеширование результатов первого запроса, но выглядит как-то костыльно. Оба выполняются примерно по минуте.

★★★★★

Последнее исправление: derlafff (всего исправлений: 2)

И два запроса внезапно станут работать быстрее в одном?

Не показывай общее количество страниц. Запрашивай LIMIT X+1, если в результате записей больше чем X, то показывай X записей и кнопку «Next page»

disarmer ★★★
()
Ответ на: комментарий от disarmer

И два запроса внезапно станут работать быстрее в одном?

Да, у меня было такое предположение, из-за того, что оба запроса выполнялись примерно одинаковое время

Не показывай общее количество страниц. Запрашивай LIMIT X+1, если в результате записей больше чем X, то показывай X записей и кнопку «Next page»

Блин, гениально. Спасибо

derlafff ★★★★★
() автор топика
Ответ на: комментарий от derlafff

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

derlafff ★★★★★
() автор топика

Offset это плохой вариант. База будет вычитывать все записи до offset. Для быстрой пагинации надо запоминать id последней записи на странице и потом запрашивать от этого id (это для упорядоченного по id запроса, для других полей так же). Число страниц точно и быстро вычислить нельзя, count(*) это просмотр всего ответа, быстро можно только приблизительную оценку у оптимизатора узнать. В принципе оно обычно точно никому не нужно.

Legioner ★★★★★
()

Медленный SELECT COUNT(*) в постгрисе это же известная фича?

https://wiki.postgresql.org/wiki/Slow_Counting

В некоторых случаях он вынужден перебирать всю таблицу

Можно или выбирать приблизительное количество записей (в статье есть ссылка на пример) или держать отдельную таблицу с количеством записей. И обновлять ее триггерами, например

Deleted
()
Ответ на: комментарий от Deleted

Спасибо.

Вообще, я кажется жутко туплю и нужно просто воткнуть эластик, а не пытаться геморроиться с постгресом.

derlafff ★★★★★
() автор топика
Ответ на: комментарий от Legioner

Увы, у меня поиск не отсортирован по уникальным полям.

derlafff ★★★★★
() автор топика

Сколько всего записей? Если мало - смотри индексы и кривизну запросов (не должно оно так тормозить). Если много - запили хранилку чтобы обновлять общее число элементов на инсерт/делит. Кешируй результаты запросов, запоминай какие элементы на какой запрос выбирались.

ya-betmen ★★★★★
()
Ответ на: комментарий от ya-betmen

6млн записей, хочу по разным полям произвольно искать. Т.е. не по всем сразу, а по параметрам. Запросы практически все уникальные, за исключением просмотров разных страниц.

Плюс, базу очень штырит от постоянных записей (но хоть как-то это победил при помощи COPY TO)

Я тут выше уже отписался — кажется, самое разумное решение в моем случае — эластик. Странно, что сразу об этом не подумал

derlafff ★★★★★
() автор топика
Последнее исправление: derlafff (всего исправлений: 2)
Ответ на: комментарий от ya-betmen

Я — единственный пользователь этого дерьма пока :) Так что и статистика, и приблизительная оценка

Делал кеширование результатов на уровне nginx, особо не помогло

derlafff ★★★★★
() автор топика
Последнее исправление: derlafff (всего исправлений: 1)
Ответ на: комментарий от derlafff

Я — единственный пользователь этого дерьма пока :) Так что и статистика, и приблизительная оценка

Понял.

ya-betmen ★★★★★
()
SELECT id AS id, count(*) OVER() AS total_count FROM table WHERE ... ORDER BY ... LIMIT ... OFFSET ... 
Frost ★★★
()
Ответ на: комментарий от Frost

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

derlafff ★★★★★
() автор топика
Последнее исправление: derlafff (всего исправлений: 1)
Ответ на: комментарий от derlafff

6млн записей, хочу по разным полям произвольно искать. Т.е. не по всем сразу, а по параметрам. Запросы практически все уникальные, за исключением просмотров разных страниц.

Плюс, базу очень штырит от постоянных записей (но хоть как-то это победил при помощи COPY TO)

Я тут выше уже отписался — кажется, самое разумное решение в моем случае — эластик. Странно, что сразу об этом не подумал

может это я не так понял, эластик это Elasticsearch или другой какой?

Frost ★★★
()
Ответ на: комментарий от derlafff

ну, и в чем тогда несостыковка? я тебе предложил полнотекстовый поиск по всем полям какие захочешь не через Elasticsearch, а через rum который и есть дополнение для postgresql полнотекстового поиска сравнимый по скорости c Elasticsearch. Про генерацию html не шло ведь речи.

Frost ★★★
()
Ответ на: комментарий от derlafff

Я просто сам на него не нарадуюсь, сейчас как раз его использую «и в хвост и в гриву», поэтому и рекомендую, он на лету добавляет элемент в поисковый индекс и много чего ещё.

Frost ★★★
()
Ответ на: комментарий от AnDoR

Спасибо, полезно.

Увы, в моем случае подходят только limit/offset и курсоры.

Разве что для частных случаев (на которых постгрес и так неплох) уместны другие способы из статьи

derlafff ★★★★★
() автор топика
Последнее исправление: derlafff (всего исправлений: 1)
Ответ на: комментарий от derlafff

А о банальной денормализации для оптимизации поиска вы не думали? Зачем лишнюю сущность в виде Эластика присобачивать?

sanwashere ★★
()
Ответ на: комментарий от sanwashere

Чтобы денормализовать, сначала надо нормализовать. А то, судя по всему, у тс все плохо.

Shadow ★★★★★
()
Ответ на: комментарий от sanwashere

Думаю над возможными решениями, никуда не тороплюсь.

Самые тяжелые запросы — это регулярки по нормальному тексту, регулярки по html (но это я придумал, как заменить на триггер+доп. поле) + поиск по тегам.

derlafff ★★★★★
() автор топика
Последнее исправление: derlafff (всего исправлений: 1)
Ответ на: комментарий от derlafff

Если речь идёт о специфичной индексации страниц, то для быстрого старта можно взять что-нибудь типа Python Soup и соответствующей структуры таблиц, если уж PostgreSQL нужен. Да и текст наверное не в общем виде нужен, - можно сразу на входе преобразовать.

sanwashere ★★
()
Ответ на: комментарий от sanwashere

Да, что-то вроде того.

Оригинальный текст мне нужен.

О нужде в стеммированном виде думаю. Боюсь, что нет — места на впс довольно мало, а все это уже довольно прилично место занимает. С другой стороны, все-таки хочется быстро статистики считать.

При задаче поиска мне регулярок хватает, при «добыче» записей я, конечно, использую lxml

derlafff ★★★★★
() автор топика
29 августа 2017 г.
Ответ на: комментарий от Frost

Помог.

Как все было:

Сначала я перенес все на RUM (в последствии — на GIN) и долго-долго дрочил на производительность запросов.

Потом плюнул, выпилил полностью постгрес и стал использовать эластик в качестве БД.

В целом простые запросы стали выполняться медленней, но все равно за секунды. Принципиально стали возможны запросы, которые из-за особенностей постгреса вообще не использовали индексы (например, полнотекстовой поиск + сортировка по произвольному полю)

derlafff ★★★★★
() автор топика
Ответ на: комментарий от derlafff

и долго-долго дрочил на производительность запросов

А чем rum то не угодил? И время запросов интересно посмотреть, можешь хоть по памяти для сравнения выложить.

Frost ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.