-
Notifications
You must be signed in to change notification settings - Fork 67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Медленный SELECT при JOIN'е по ключу партиционирования #188
Comments
Потому что при join получается параметризированный запрос с условием типа
Фильтровать по константным условиям. Возможно, партицировать по другому ключу, значение которого вам известно до выполнения запроса. |
При планировании же выбирается |
Прошло какое-то время и проблема сама собой устранилась. Сейчас результаты вот такие: EXPLAIN (ANALYZE, BUFFERS)
SELECT
o.number,
v.id,
v.cost
FROM
orders o
JOIN valuation v ON v.id = o.paid_online_on_delivery_id
WHERE
o.number = '1105047037';
Неоднократно пытался воспроизвести то же самое. Удалял полностью таблицы, создавал заново, партиционировал и заполнял данными. Проблема не воспроизвелась. Похоже, что это какой-то внешний фактор, например autovacuum. Но как это однозначно определить пока непонятно, т.к. воспроизвести повторно пока не удаётся. |
Проблема снова появилась. Постарался максимум информации вытащить в моменте. Проблемные запросыЗапрос № 1orders=# EXPLAIN (ANALYZE, BUFFERS)
SELECT
o.number,
v.id,
v.cost
FROM
orders o
JOIN valuation v ON v.id = o.paid_online_on_delivery_id
WHERE
o.number = '1105047037';
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=0.99..10.64 rows=1 width=22) (actual time=0.944..0.944 rows=0 loops=1)
Buffers: shared hit=2 read=2
-> Index Scan using udx_orders_number on orders o (cost=0.56..5.38 rows=1 width=18) (actual time=0.943..0.943 rows=0 loops=1)
Index Cond: (number = '1105047037'::text)
Buffers: shared hit=2 read=2
-> Custom Scan (RuntimeAppend) (cost=0.43..5.24 rows=1 width=12) (never executed)
Prune by: (o.paid_online_on_delivery_id = v.id)
Planning time: 95693.529 ms
Execution time: 1.420 ms
(9 строк) Запрос № 2orders=# EXPLAIN (ANALYZE, BUFFERS)
SELECT
o.number,
v.id,
v.cost
FROM
orders o
JOIN valuation v ON v.id = o.paid_online_on_delivery_id
WHERE
o.number = '1104047036';
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=0.99..10.64 rows=1 width=22) (actual time=2.251..2.252 rows=1 loops=1)
Buffers: shared hit=2 read=8
-> Index Scan using udx_orders_number on orders o (cost=0.56..5.38 rows=1 width=18) (actual time=1.140..1.141 rows=1 loops=1)
Index Cond: (number = '1104047036'::text)
Buffers: shared hit=2 read=3
-> Custom Scan (RuntimeAppend) (cost=0.43..5.24 rows=1 width=12) (actual time=1.070..1.070 rows=1 loops=1)
Prune by: (o.paid_online_on_delivery_id = v.id)
Buffers: shared read=5
-> Index Scan using valuation_3762_pkey on valuation_3762 v_1 (cost=0.57..5.38 rows=1 width=12) (actual time=1.069..1.069 rows=1 loops=1)
Index Cond: (id = o.paid_online_on_delivery_id)
Buffers: shared read=5
Planning time: 3834.163 ms
Execution time: 2.333 ms
(13 строк) Запрос № 3orders=# EXPLAIN (ANALYZE, BUFFERS)
SELECT
o.number,
v.id,
v.cost
FROM
orders o
JOIN valuation v ON v.id = o.paid_online_on_delivery_id
WHERE
o.number = '1';
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=0.99..10.64 rows=1 width=22) (actual time=0.092..0.093 rows=1 loops=1)
Buffers: shared hit=9
-> Index Scan using udx_orders_number on orders o (cost=0.56..5.38 rows=1 width=18) (actual time=0.031..0.031 rows=1 loops=1)
Index Cond: (number = '1'::text)
Buffers: shared hit=5
-> Custom Scan (RuntimeAppend) (cost=0.43..5.24 rows=1 width=12) (actual time=0.017..0.017 rows=1 loops=1)
Prune by: (o.paid_online_on_delivery_id = v.id)
Buffers: shared hit=4
-> Index Scan using valuation_3760_pkey on valuation_3760 v_1 (cost=0.42..5.24 rows=1 width=12) (actual time=0.016..0.016 rows=1 loops=1)
Index Cond: (id = o.paid_online_on_delivery_id)
Buffers: shared hit=4
Planning time: 4337.439 ms
Execution time: 0.204 ms
(13 строк) Активные процессыselect * from pg_stat_activity where datname = 'orders' and state = 'active'; Срез № 1: https://gist.github.com/JanMihail/5d113b5b9f6d48b422658e3130b23a00#file-process-list-1-csv Блокировкиselect l.database, d.datname, l.relation, c.relname,
l.locktype,
l.virtualxid, l.virtualtransaction, l.transactionid,
l.pid, l.mode, l.granted,
c.relacl
from pg_locks as l
LEFT JOIN pg_database AS d ON l.database= d.oid
LEFT JOIN pg_class AS c ON l.relation = c.oid
where d.datname = 'orders'
order by c.relname Срез № 1: https://gist.github.com/JanMihail/5d113b5b9f6d48b422658e3130b23a00#file-pg-locks-1-csv |
После того как прошли все autovacuum'ы, время планирования стало приемлемым. Запрос № 1EXPLAIN (ANALYZE, BUFFERS)
SELECT
o.number,
v.id,
v.cost
FROM
orders o
JOIN valuation v ON v.id = o.paid_online_on_delivery_id
WHERE
o.number = '1105047037';
Nested Loop (cost=0.99..10.64 rows=1 width=22) (actual time=0.737..0.737 rows=0 loops=1)
Buffers: shared hit=2 read=2
-> Index Scan using udx_orders_number on orders o (cost=0.56..5.38 rows=1 width=18) (actual time=0.735..0.735 rows=0 loops=1)
Index Cond: (number = '1105047037'::text)
Buffers: shared hit=2 read=2
-> Custom Scan (RuntimeAppend) (cost=0.43..5.24 rows=1 width=12) (never executed)
Prune by: (o.paid_online_on_delivery_id = v.id)
Planning time: 113.575 ms
Execution time: 0.876 ms Запрос № 2EXPLAIN (ANALYZE, BUFFERS)
SELECT
o.number,
v.id,
v.cost
FROM
orders o
JOIN valuation v ON v.id = o.paid_online_on_delivery_id
WHERE
o.number = '1104047036';
Nested Loop (cost=0.99..10.64 rows=1 width=22) (actual time=3.743..3.746 rows=1 loops=1)
Buffers: shared hit=2 read=8
-> Index Scan using udx_orders_number on orders o (cost=0.56..5.38 rows=1 width=18) (actual time=1.152..1.154 rows=1 loops=1)
Index Cond: (number = '1104047036'::text)
Buffers: shared hit=2 read=3
-> Custom Scan (RuntimeAppend) (cost=0.43..5.24 rows=1 width=12) (actual time=2.544..2.545 rows=1 loops=1)
Prune by: (o.paid_online_on_delivery_id = v.id)
Buffers: shared read=5
-> Index Scan using valuation_3762_pkey on valuation_3762 v_1 (cost=0.57..5.38 rows=1 width=12) (actual time=2.541..2.541 rows=1 loops=1)
Index Cond: (id = o.paid_online_on_delivery_id)
Buffers: shared read=5
Planning time: 6.817 ms
Execution time: 3.823 ms Запрос № 3EXPLAIN (ANALYZE, BUFFERS)
SELECT
o.number,
v.id,
v.cost
FROM
orders o
JOIN valuation v ON v.id = o.paid_online_on_delivery_id
WHERE
o.number = '1';
Nested Loop (cost=0.99..10.64 rows=1 width=22) (actual time=5.755..5.761 rows=1 loops=1)
Buffers: shared hit=1 read=8
-> Index Scan using udx_orders_number on orders o (cost=0.56..5.38 rows=1 width=18) (actual time=2.087..2.091 rows=1 loops=1)
Index Cond: (number = '1'::text)
Buffers: shared hit=1 read=4
-> Custom Scan (RuntimeAppend) (cost=0.43..5.24 rows=1 width=12) (actual time=3.497..3.498 rows=1 loops=1)
Prune by: (o.paid_online_on_delivery_id = v.id)
Buffers: shared read=4
-> Index Scan using valuation_3760_pkey on valuation_3760 v_1 (cost=0.42..5.24 rows=1 width=12) (actual time=3.493..3.493 rows=1 loops=1)
Index Cond: (id = o.paid_online_on_delivery_id)
Buffers: shared read=4
Planning time: 10.663 ms
Execution time: 5.858 ms ВыводНельзя запускать неблокирующий перенос данных ( Как я в итоге поступил1. Создал партиции без переноса данныхSELECT create_range_partitions('valuation', 'id', 1::bigint, 1000000::bigint, NULL, false);
SELECT create_range_partitions('client', 'id', 1::bigint, 1000000::bigint, NULL, false);
SELECT create_range_partitions('online_shop', 'id', 1::bigint, 1000000::bigint, NULL, false);
SELECT create_range_partitions('address', 'id', 1::bigint, 1000000::bigint, NULL, false); 2. Запустил перенос данных из одной таблицы в партицииSELECT partition_table_concurrently('valuation', 10000); 3. Дождался завершения переносаПериодически выполняю запрос, пока он не будет возвращать пустоту SELECT * FROM pathman_concurrent_part_tasks; 4. Отключаю родительскую таблицу из планированияSELECT set_enable_parent('valuation', FALSE); 5. Жду пока все процессы autovacuum завершатся на всех партициях и на родительской таблицеВажно!!! Обязательно нужно дождаться, завершения работы autovacuum на родительской таблице, т.к. из неё ушли все данные и количество мёртвых строк очень велико. 6. Повторяю шаги с 2 по 5 для остальных таблиц.P.S. Если сервер достаточно мощный, то, возможно, у вас всё будет нормально и при переносе данных сразу из четырёх таблиц. Конкретно в моём случае способ описанный выше сработал отлично. |
@JanMihail тут надо отметить, что долгое время планирования происходит при первом запросе в новой сессии при большом кол-ве секций, что обусловлено прогревом локального кэша конфигурацией секционирования. Вы лучше приложите dump схемы ваших таблиц и секций, если это возможно, чтобы мы попатались воспроизвести ваш кейс. |
@maksm90 Высылаю dump схемы таблиц orderschema.custom.zip |
@JanMihail спасибо. Ваш случай воспроизводится только в случает множества активных vacuum-процессов в базе? Не могли бы вы собрать профиль ожиданий от pg_wait_sampling для процессa, у которого наблюдается такое долгое время планирования? Мы могли бы для вас пропатчить это расширение, чтобы не прописывать его в |
@maksm90, воспроизводится даже когда нет активных vacuum-процессов. При большом количестве запросов база покрывается AccessShareLock блокировками. Возможно причина в этом. Высылаю профиль ожиданий от pg_wait_sampling |
@JanMihail спасибо за сэмплы. По ним видно, что у вас contention за мьютексы менеджера блокировок. Чтобы попытаться что-нибудь сделать, нам надо будет воспроизвести вашу нагрузку. Опишите конфигурацию вашей системы и примерную нагрузку на базу.
Да, действий с блокировками так много, что возникает проблема в их обслуживании. Не могли бы вы указать, какое приблизительное кол-во блокировок берётся в моменты зависания запросов? Несмотря на предоставленные сэмплы, хотелось бы оценить временную долю ожиданий. Поэтому не могли бы вы ещё раз снять сэмплы после того как мы пропатчим |
Описание проблемы
Есть 2 таблицы со следующей структурой:
Связь
Количественные показатели таблиц
Таблица valuation разбита на партиции по диапазонам. Ключ партиционирования - id. Всего 100 партиций.
Результат запроса: https://gist.github.com/JanMihail/fd45cfef5ac24fe960e91a92c648b266#file-partitions-csv
Выполняю такой запрос
Результат
Из результатов вижу, что на планирование запроса ушло целых 12 секунд 748 миллисекунд.
Теперь я пробую сделать то же самое, но указываю в запросе конкретную партицию valuation_3513 из предыдущего EXPLAIN и смотрю на результаты.
Результат
Время планирования резко уменьшилось до 0.351 миллисекунд. Получается, что в первом запросе 12 секунд потратилось на поиск нужной партиции. Тогда я делаю следующий запрос.
Результат
При таком запросе на выбор нужной партиции затрачено всего 0.128 миллисекунд. Почему при JOIN по id (ключ партиционирования) время планирования резко подскакивает до 12 секунд? Что можно сделать, чтобы время планирования стало приемлемым?
Что я пытался делать:
Ничего из этого не дало мне нужного результата.
Прошу помогите мне решить проблему.
Environment
SELECT * FROM pg_extension
SELECT version()
SELECT pgpro_version()
SQL Error [42883]: ERROR: function pgpro_version() does not exist
SELECT pgpro_edition()
SQL Error [42883]: ERROR: function pgpro_edition() does not exist
SELECT get_pathman_lib_version()
SQL Error [42883]: ERROR: function get_pathman_lib_version() does not exist
The text was updated successfully, but these errors were encountered: