172 похожих чатов

Друзья такой вопрос Есть в таблице 2 поля: id и el_id.

id - первичный ключ, а по el_id создал индекс
Я делаю запрос Х по типу SELECT el_id FROM table ORDER BY el_id DESC; Делается он достаточно долго.
Данные сейчас лежат так: непоследовательно идет id, а el_id вперемешку
Сейчас в таблице 150 241 790 строк.

Как увеличить скорость?
В таблицу планируется, что буду класть данные так, что поле el_id будет последовательно увеличиваться (то есть не будет вперемешку)
Если я сделаю копию этой таблицы, где я уберу оригальное полe id , а поле el_id сделаю первичным ключом, и сложу так, что el_id будет увеличиваться от минимального к максимальному, то

psql (PostgreSQL) 11.9 (Debian 11.9-0+deb10u1)
запрос: https://pastebin.com/kaV0wLkw

60 ответов

13 просмотров

https://t.me/pgsql/303899

Даниил-Агниашвили Автор вопроса
Даниил-Агниашвили Автор вопроса
Даниил Агниашвили
screenshot

https://t.me/pgsql/303899 Текстом покажите, естественно.

Даниил Агниашвили
https://pastebin.com/kaV0wLkw вроде всё

Нет, не всё, explain очевидно обрезан.

Даниил-Агниашвили Автор вопроса
Даниил-Агниашвили Автор вопроса

в равке есть

Даниил Агниашвили
https://pastebin.com/kaV0wLkw вроде всё

И из 4 запрошенных пунктов Вам "удался" только один. Видимо, помощь Вам всё-таки не нужна.

Даниил Агниашвили
https://pastebin.com/kaV0wLkw вроде всё

Кстатида, Ярослав, кроме прочего, тонко намекает что вы ещё и buffers в explain забыли.

Даниил Агниашвили
https://pastebin.com/kaV0wLkw вроде всё

Это я просто подумал "почему один, когда вроде два?"

Даниил-Агниашвили Автор вопроса
Yaroslav Schekin
И из 4 запрошенных пунктов Вам "удался" только оди...

https://pastebin.com/XMQQEPE7 Хм, всё полностью выложил сюды

Даниил Агниашвили
https://pastebin.com/XMQQEPE7 Хм, всё полностью вы...

Вообще, с планом проблема, конечно. То есть я бы, представляя предметную область -- сначала хотел бы чтобы выбрали все houses по указанным id_addresses (их там в пределах пары десятков тысяч будет), а потом ужэ по id_house сканировало... А тут он явно наоборот ищет, неудивительно с такой селективностью что пол-таблицы cache_table по индэксу переворачивает. Ну и, из очевидного -- моя идея плохо сработает если нет индэкса на cache_table(house_id, index_id). Так что можно попробовать его создать (вместе c analyze), и посмотреть, пересмотрит ли Postgres своё поведение.

Даниил Агниашвили
https://pastebin.com/XMQQEPE7 Хм, всё полностью вы...

Наконец-то, спасибо! ;) Далее, вот Вы тут https://t.me/pgsql/308470 писали: > Данные сейчас лежат так: непоследовательно идет id, а el_id вперемешку > Сейчас в таблице 150 241 790 строк. > В таблицу планируется, что буду класть данные так, что поле el_id будет последовательно увеличиваться (то есть не будет вперемешку) Про какую именно таблицу была речь? cache_table или houses? Что сейчас видно — это то, что Вы "попали" на т.н. early abort plan — нужно посмотреть, что дают альтернативы, и для этого нужно больше знать про эти данные (см. хотя бы вопросы коллег выше).

Даниил Агниашвили
Имею ввиду про таблицу cache_table

И да, а где Foreign Key cache_table(id_house) REFERENCES houses? Не выложили или его нет?

Даниил Агниашвили
Его нет

А почему (это может влиять на оценки, например)? Тем не менее — давайте больше информации. ;)

Даниил Агниашвили
Его нет

Я вот это имею в виду, например: > сначала хотел бы чтобы выбрали все houses по указанным id_addresses (их там в пределах пары десятков тысяч будет) Это действительно так? Ну и вообще, сколько rows в таблице houses, сколько отбирается отдельно по условиям (без LIMIT) из неё и всего из cache_table... особенно, какие оценки этого всего. Т.е., например: EXPLAIN (ANALYZE, BUFFERS) SELECT id_addresses FROM houses WHERE t2.id_addresses IN (...); Ну и так далее.

Даниил-Агниашвили Автор вопроса
Yaroslav Schekin
А почему (это может влиять на оценки, например)? ...

Еще больше информации. Тут инфа о кейсе, о полях и последних запросах которые я делал, https://docs.google.com/document/d/1ahpmWn1-QZ8a2cD54prkpH0ixpJP78F1BiErvMPztnQ/edit?usp=sharing

Даниил Агниашвили
Еще больше информации. Тут инфа о кейсе, о полях и...

Читаю... > сделал vacuum full houses, cache_table А сделайте пока VACUUM ANALYZE houses, cache_table; — оценки rows какие-то плохие, лучше начать с этого.

Даниил Агниашвили
Хорошо

Ну и вот это (например): -> Index Scan using houses_index on houses t2 (cost=0.56..909020.31 rows=1242288 width=4) (actual time=0.915..255.822 rows=71594 loops=3) Index Cond: (id_addresses = ANY ('{1300337,ПЕРЕЧИСЛЕНИЕ,17572575}'::integer[])) Buffers: shared hit=60662 read=12015 как бы намекает нам (конкретно, наличие shared read при таком объёме считанного), что на этом сервере плохо либо с "железом", либо с tuning OS/PostgreSQL. Там вообще что-то настраивалось?

Даниил-Агниашвили Автор вопроса
Yaroslav Schekin
Ну и вот это (например): -> Index Scan using hous...

Сервер https://ru.hetzner.com/hosting/produkte_rootserver/ax41-nvme/ postgresql.conf: https://pastebin.com/1tVnYLGz pg_ctl.conf пустой https://pastebin.com/ueSBPE4J Мне кажется, что из решения - надо попробовать 1) сделать так, чтобы выборка была учитывая дату, тогда получится существенно уменьшить размер области поиска 2) возможно, переделать эти две таблицы, мб добавить партиции ( по датам опять же или по регионам, мб сделать поле Количество, чтобы делать sum() (?))

Даниил-Агниашвили Автор вопроса
Даниил Агниашвили
Сервер https://ru.hetzner.com/hosting/produkte_ro...

Явно не настраивался, да. Если там работает только PostgreSQL, то: shared_buffers = 128MB # min 128kB на таком железе — это просто смешно. А вот work_mem = 500MB # min 64kB возможно, слишком много. Попробуйте хоть http://pgconfigurator.cybertec.at/ для того, чтобы получить ориентировочные значения параметров. И да, synchronous_commit = off # synchronization level; Возможные потери транзакций не волнуют? ;) include_dir = 'conf.d' # include files ending in '.conf' from И вот тут ничего нет, кстати? Ну и проще / надёжнее с работающего сервера запросить pg_settings, чем в файлах копаться. И да, если уж заниматься tuning, то начинать стоит с базовой настройки OS... вообще, см. http://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server > Мне кажется, что из решения - надо попробовать А мне кажется, что прежде чем что-то делать, стоит разобраться с проблемой. ;) > возможно, переделать эти две таблицы, мб добавить партиции Чтобы всё стало ещё хуже? Кто Вам сказал, что partitioning — это fairy dust (зачастую оно делает всё только хуже)?

Даниил-Агниашвили Автор вопроса
Yaroslav Schekin
Явно не настраивался, да. Если там работает только...

конфиг файл взял с домашнего сервака, который настраивал под себя. понял

Хмм... при чём тут реплика? При таких настройках committed transactions могут быть потеряны при обычном crash / immediate shutdown. Впрочем, им виднее, устраивает это или нет...

Даниил Агниашвили
конфиг файл взял с домашнего сервака, который наст...

В общем, как перенастроите (если есть возможность), и выполнится VACUUM — можно будет продолжить...

Даниил-Агниашвили Автор вопроса
Даниил-Агниашвили Автор вопроса
Даниил Агниашвили
Еще больше информации. Тут инфа о кейсе, о полях и...

Привет еще раз! Настроил вот конфиг https://pastebin.com/Pq3G3wgK Снова Сделал запрос на EXPLAIN (ANALYZE, BUFFERS) (без LIMIT) Получился такой выхлоп https://pastebin.com/VWf4mCRt док с предыдущими тут https://docs.google.com/document/d/1ahpmWn1-QZ8a2cD54prkpH0ixpJP78F1BiErvMPztnQ/edit?usp=sharing

Даниил-Агниашвили Автор вопроса

лишь сам постгрес) сттоит сервер перезапусть?

Даниил-Агниашвили Автор вопроса

вакуум фулл на всю БД я не смогу сделать, на сервере меньше 600гб занято данными, 300гб свободно или только фулл тех злосчастных двух таблиц? cache_table + houses

Даниил Агниашвили
Привет еще раз! Настроил вот конфиг https://paste...

Хмм... а точно настройки применились (опять-таки, я же Вам советовал смотреть в pg_settings — может, в include_dir = 'conf.d' всё, что Вы поставили, "перебивается")? > или только фулл тех злосчастных двух таблиц? cache_table + houses Хмм... а что Вы до этого делали? Так VACUUM ANALYZE после VACUUM FULL был выполнен или нет?

Даниил-Агниашвили Автор вопроса
Yaroslav Schekin
Хмм... а точно настройки применились (опять-таки, ...

conf.d пустая директория. надо её удалить? сейчас выполнил: daniilak=# vacuum full verbose houses; INFO: vacuuming "public.houses" INFO: "houses": found 0 removable, 49726993 nonremovable row versions in 1056642 pages DETAIL: 0 dead row versions cannot be removed yet. CPU: user: 139.84 s, system: 5.61 s, elapsed: 147.62 s. VACUUM ____ daniilak=# vacuum full verbose cache_table; INFO: vacuuming "public.cache_table" INFO: "cache_table": found 0 removable, 150241790 nonremovable row versions in 956955 pages DETAIL: 0 dead row versions cannot be removed yet. CPU: user: 182.87 s, system: 4.32 s, elapsed: 189.11 s. VACUUM daniilak=# vacuum analyse houses, cache_table; VACUUM

Даниил Агниашвили
conf.d пустая директория. надо её удалить? сейчас ...

> conf.d пустая директория. надо её удалить? Не надо. Просто откуда нам знать, есть там что-то или нет? ;) daniilak=# vacuum analyse houses, cache_table; VACUUM Это только сейчас было сделано? Если да, тогда интересуют новые планы запросов.

Даниил-Агниашвили Автор вопроса
Yaroslav Schekin
> conf.d пустая директория. надо её удалить? Не н...

Логично) до этого тоже делал этот запрос. в любом случае заново сделал EXPLAIN (ANALYZE, BUFFERS) https://pastebin.com/fGAaLb2U

Даниил Агниашвили
Логично) до этого тоже делал этот запрос. в любом...

Послушайте... немного не в тему, но что-то тут не так, мне кажется (но не факт). Что выдаёт SHOW shared_buffers;? И что происходит при повторении запроса (меняются ли shared hit / read в плане)?

Даниил Агниашвили
screenshot SHOW shared_buffers; == 16GB меняются

Значит, просто не закешировалось, ну и ладно. Так, а у: SELECT t1.index_id FROM cache_table AS t1 WHERE EXISTS ( SELECT 1 FROM houses AS t2 WHERE t1.id_house = t2.id AND t2.id_addresses IN (...) ); тот же план, на всякий случай? А, и по tuning — с таким "железом" стоит поставить ещё cpu_tuple_cost = 0.1.

Даниил-Агниашвили Автор вопроса
Yaroslav Schekin
Значит, просто не закешировалось, ну и ладно. Так,...

у этого запроса надо еще сделать ORDER BY t1.index_id; выхлоп: два раза запускал https://pastebin.com/Ff65xwQd запустил тот же запрос, но без ORDER BY (для интереса) выхлоп: https://pastebin.com/v3pZtQ4J

Даниил Агниашвили
у этого запроса надо еще сделать ORDER BY t1.index...

> у этого запроса надо еще сделать ORDER BY t1.index_id; А пропустил, извините. > выхлоп: два раза запускал Да, план тот же. А: > по tuning — с таким "железом" стоит поставить ещё cpu_tuple_cost = 0.1. Вы пробовали? Если план от этого не изменится, то уже нужно либо улучшать статистику по houses.id_addresses, либо создавать подходящие индексы.

Даниил-Агниашвили Автор вопроса
Yaroslav Schekin
> у этого запроса надо еще сделать ORDER BY t1.ind...

cpu_tuple_cost = 0.1. - сейчас добавлю, после этого надо делать вакуум фул/анализ?

Даниил Агниашвили
cpu_tuple_cost = 0.1. - сейчас добавлю, после этог...

Это можно сделать прямо в сессии: SET cpu_tuple_cost = 0.1; EXPLAIN (ANALYZE, BUFFERS) SELECT ... > после этого надо делать вакуум фул/анализ? Нет. И вообще, не выполняйте VACUUM FULL "просто так" — это делается только в "крайних" случаях.

Даниил-Агниашвили Автор вопроса
Yaroslav Schekin
Это можно сделать прямо в сессии: SET cpu_tuple_co...

Хм, а чем грозит выполнение просто так?

Даниил Агниашвили
Хм, а чем грозит выполнение просто так?

Блокированием всех запросов, которые обращаются к этой таблице; потерей возможностей index-only scans и HOT updates; отставанием реплик; да и просто напрасной нагрузкой.

Даниил Агниашвили
Сделал: https://pastebin.com/eFYmA9u6

Т.е. остаётся либо статистика: ALTER TABLE houses ALTER COLUMN id_addresses SET STATISTICS 10000; VACUUM ANALYZE houses; либо индексы, в "худшем" случае: CREATE INDEX ON houses(id_addresses, id); CREATE INDEX ON cache_table(id_house, index_id);

Даниил-Агниашвили Автор вопроса
Yaroslav Schekin
Т.е. остаётся либо статистика: ALTER TABLE houses ...

cache_table(id_house, index_id) индекс уже есть, а SET STATISTICS - где можно подробнеее от об этом почтитать

Даниил Агниашвили
cache_table(id_house, index_id) индекс уже есть, ...

В том, что Вы приводили, этого индекса не было. А прочитать можно в https://www.postgresql.org/docs/current/planner-stats.html#id-1.5.13.5.3 и в https://www.postgresql.org/docs/current/sql-altertable.html , например.

Yaroslav Schekin
Хмм... зачем?

У него там limit 100 по этому index_id, такое количество результатов скорее всего найдётся довольно скоро, притом примерно index_only scanом. А при выборке по id_house -- ему придётся сначала все выбрать, потом отсортировать.

Ilya Anfimov
У него там limit 100 по этому index_id, такое коли...

А такой вариант уже был, вот он: Limit (cost=1001.16..14871.56 rows=100 width=4) (actual time=1053.867..2323.809 rows=100 loops=1) Buffers: shared hit=1799709 read=297868 -> Gather Merge (cost=1001.16..520607739.31 rows=3753366 width=4) (actual time=1053.865..2323.786 rows=100 loops=1) Workers Planned: 2 Workers Launched: 2 Buffers: shared hit=1799709 read=297868 -> Nested Loop (cost=1.14..520173507.72 rows=1563902 width=4) (actual time=1019.601..2001.823 rows=46 loops=3) Buffers: shared hit=1799709 read=297868 -> Parallel Index Scan Backward using cache_index_id on cache_table t1 (cost=0.57..118451325.04 rows=62600747 width=8) (actual time=0.035..139.189 rows=124142 loops=3) Buffers: shared hit=124767 read=108346 -> Index Scan using houses_pkey on houses t2 (cost=0.56..6.42 rows=1 width=4) (actual time=0.015..0.015 rows=0 loops=372426) Index Cond: (id = t1.id_house) Filter: (id_addresses = ANY ('{1300337,...}'::integer[])) Rows Removed by Filter: 1 Buffers: shared hit=1674942 read=189522 Planning Time: 4.559 ms Execution Time: 2323.861 ms И в нём: -> Index Scan using houses_pkey on houses t2 (cost=0.56..6.42 rows=1 width=4) (actual time=0.015..0.015 rows=0 loops=372426) Т.е. для того, чтобы найти 100 подходящих записей, просканировать приходится 372426. По-моему, это не "довольно скоро", да и неизвестно, "как повезёт" для следующих 100 (эти LIMIT ... OFFSET тут для pagination). А вот вариант с выборкой по houses: > Index Scan using houses_index on houses t2 (cost=0.56..851821.48 rows=970332 width=4) (actual time=0.922..161.247 rows=71594 loops=5) Т.е. вполне может быть лучше "сначала всё выбрать, потом сортировать".

Yaroslav Schekin
А такой вариант уже был, вот он: Limit (cost=100...

Если это будет 372426 index-only access, или, точнее index+hash join filter в памяти -- то это гораздо меньшэ, чем 70k index+heap access. А в том случае -- там таки был index access на cache, по нему index access на house и для него heap access на house. Хотя, конечно, учитывая что конкретно сейчас почти всё в памяти -- можэт и не будет это выгодным.

Даниил Агниашвили
https://pastebin.com/XMQQEPE7 Хм, всё полностью вы...

Кстати, три момента подумалось тут с утра. Первый -- что если это всё равно cache какой-то (ну, основная таблица так называется), и отношэния к houses у него подчинённые (id-то там primary key) -- то можно id_addresses в этот кэш запихивать. Не то, чтобы сильно поможэт в скорости (там и так вроде достаточно быстро) -- но как минимум снимет немного всяких плохих возможностей построения запроса. И ещё можно будет попробовать всякие изощрённые индэксы поприменять, чтобы всё-таки в ограниченное количество page access это укладывалось. Второй -- это что не всякий клиент руками набьёт тысячу индэксов.... А там точно нет каких-то справочников, из которых эти громадные списки берутся? А то если есть -- то можно как раз значение этого справочника добавить в таблицу, и искать по нему. Тогда по индэксу (справочник_id, index_id) оно искаться будет элементарно. И третий -- если справочников нет, запросы большые, клиенты тожэ автоматы, но паджынацыя, вот это всё -- то можно результаты запросов кэшыровать на сервере. На полчаса там, допустим, не знаю. Забабахать криптографических хэш от условия (списка индэксов, сортированного), по нему выбирать -- есть ужэ результат в кэшэ или как. Во временных таблицах (или во временной таблицэ). У которой ужэ будет индэкс прямо по результатам отсортированный.

Ilya Anfimov
Если это будет 372426 index-only access, или, точн...

Не на то это всё рассчитывалось, но можно попробовать так и так. А у меня с утра появился вопрос к @daniilak — а мы вообще настоящий запрос оптимизируем? Неужели там действительно выбирается только t1.index_id? И если да, то ORDER BY t1.index_id DESC тупо не подходит для pagination — это же не даёт детерминированного порядка выборки.

Даниил-Агниашвили Автор вопроса
Ilya Anfimov
Кстати, три момента подумалось тут с утра. Первый...

Благодарю. Клиенты еще пока не понятно, как они будут смотреть. Справочник вполне себе можно добавить, надо только подумать Возможно еще обращусь с новыми идеями)

Даниил-Агниашвили Автор вопроса
Ilya Anfimov
Кстати, три момента подумалось тут с утра. Первый...

Привет снова! А я подумал еще про одну деталь У меня в таблице cache_house есть поля: year, month и day. Все типа smallint. Кстати, надо просто создать взамен 1 колонку типа date. То я могу сделать ключ по дате +index_id + house_id и тогда в случае поиска сделать обязательным выбор промежутка даты, то тогда будет всё круто. Еще можно будет закешировать на каждый запрос общее количество элементов, чтобы показать, мол, в других датах еще есть записи

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

Обсуждают сегодня

а через ESC-код ?
Alexey Kulakov
29
30500 за редактор? )
Владимир
47
Чёт не понял, я ж правильной функцией воспользовался чтобы вывести отладочную информацию? но что-то она не ловится
notme
18
У меня есть функция где происходит это: write_bit(buffer, 1); write_bit(buffer, 0); write_bit(buffer, 1); write_bit(buffer, 1); write_bit(buffer, 1); w...
~
13
Недавно Google Project Zero нашёл багу в SQLite с помощью LLM, о чём достаточно было шумно в определённых интернетах, которые сопровождались рассказами, что скоро всех "ибешни...
Alex Sherbakov
5
program test; {$mode delphi} procedure proc(v: int32); overload; begin end; procedure proc(v: int64); overload; begin end; var x: uint64; begin proc(x); end. Уж не знаю...
notme
6
Как передать управляющий символ в открытую через CreateProcess консоль? Собсна, есть процедура: procedure TRedirectThread.WriteData(Data: OEMString); var Written: Cardinal;...
Serjone
6
вы делали что-то подобное и как? может есть либы готовые? увидел картинку нокода, где всё линиями соединено и стало интересно попробовать то же в ddl на lua сделать. решил с ч...
Victor
8
Ребят в СИ можно реализовать ООП?
Николай
33
Подскажите пожалуйста, как в CustomDrawCell(Sender: TcxCustomGridTableView; ACanvas: TcxCanvas; AViewInfo: TcxGridTableDataCellViewInfo; var ADone: Boolean); получить наз...
A Z
7
Карта сайта