p."attrs" @> '{337}'
+------------------------------------------------------------------------------------------------------------------------------------------+
|QUERY PLAN |
+------------------------------------------------------------------------------------------------------------------------------------------+
|ProjectSet (cost=73.00..5004.83 rows=58060 width=4) (actual time=0.841..316.285 rows=155400 loops=1) |
| Output: unnest(attrs) |
| -> Bitmap Heap Scan on public.product_v2 p (cost=73.00..4670.98 rows=5806 width=92) (actual time=0.836..16.179 rows=5974 loops=1) |
| Output: attrs |
| Recheck Cond: (p.attrs @> '{337}'::integer[]) |
| Heap Blocks: exact=3245 |
| -> Bitmap Index Scan on product_v2_attrs_index (cost=0.00..71.55 rows=5806 width=0) (actual time=0.543..0.545 rows=5974 loops=1)|
| Index Cond: (p.attrs @> '{337}'::integer[]) |
|Planning Time: 0.070 ms |
|Execution Time: 598.275 ms |
+------------------------------------------------------------------------------------------------------------------------------------------+
из 6т записей получаешь 155т. не думаю, что тут можно что-то радикально ускорить
Без переписывания кода постгреса -- это не слишком вероятно. (Лучшэ избавиться от необходимости unnest. Перенести его в код приложэния, если обработка на сервере не нужна -- либо нормализовать базу, если нужна).
Нормализация делает больно + усложняет выборки
Это заблуждение, делает больно в основном хранение данных в виде массивов
Ну, вам и так больно...
А какая версия PostgreSQL ( SELECT version(); ) и распределение количества элементов в массивах? Потому что (у меня v15.3) вот это: CREATE UNLOGGED TABLE product_v2 ( id bigint NOT NULL , attrs int[] NOT NULL ); INSERT INTO product_v2(id, attrs) SELECT 1 + n / 28 AS id, array_agg(n / 6000 + n % 800) FROM generate_series(1, 8000000) AS g(n) GROUP BY 1 ORDER BY id; CREATE INDEX ON product_v2 USING gin(attrs); VACUUM ANALYZE product_v2; Даёт вот такой план: ProjectSet (cost=55.46..6496.08 rows=50920 width=4) (actual time=4.158..16.461 rows=142576 loops=1) Output: unnest(attrs) -> Bitmap Heap Scan on public.product_v2 p (cost=55.46..6203.29 rows=5092 width=135) (actual time=4.146..8.404 rows=5092 loops=1) Output: id, attrs Recheck Cond: (p.attrs @> '{678}'::integer[]) Heap Blocks: exact=3096 -> Bitmap Index Scan on product_v2_attrs_idx (cost=0.00..54.19 rows=5092 width=0) (actual time=2.400..2.400 rows=5092 loops=1) Index Cond: (p.attrs @> '{678}'::integer[]) Planning Time: 0.397 ms Execution Time: 19.457 ms Обратите внимание, что почти все числа очень близки к Вашему (или даже немного хуже). А вот именно время unnest() почему-то сильно отличается (поэтому и вопросы выше).
Странно. А по второму вопросу (может, там сильно разное количество элементов в разных записях, а не по ≈ 28, как в моём примере)? Ну и Вы можете просто попробовать показанный пример у себя — сравнимое получится время исполнения или совсем другое?
+--------------------------------------------------------------------------------------------------------------------------------------------+ |QUERY PLAN | +--------------------------------------------------------------------------------------------------------------------------------------------+ |Gather (cost=1000.00..9233.88 rows=5371 width=4) (actual time=0.177..472.081 rows=142576 loops=1) | | Output: (unnest(attrs)) | | Workers Planned: 2 | | Workers Launched: 2 | | -> ProjectSet (cost=0.00..7696.78 rows=22380 width=4) (actual time=0.020..194.753 rows=47525 loops=3) | | Output: unnest(attrs) | | Worker 0: actual time=0.020..282.544 rows=69328 loops=1 | | Worker 1: actual time=0.019..289.857 rows=70700 loops=1 | | -> Parallel Seq Scan on public.product_v2 p (cost=0.00..7568.10 rows=2238 width=136) (actual time=0.014..27.503 rows=1697 loops=3)| | Output: id, attrs | | Filter: (p.attrs @> '{678}'::integer[]) | | Rows Removed by Filter: 93541 | | Worker 0: actual time=0.013..39.872 rows=2476 loops=1 | | Worker 1: actual time=0.014..36.161 rows=2525 loops=1 | |Planning Time: 0.060 ms | |Execution Time: 919.878 ms | +--------------------------------------------------------------------------------------------------------------------------------------------+ кверя select unnest(p."attrs") from product_v2 p where p."attrs" @> '{678}'
А Вы точно выполнили VACCUM ANALYZE (какие-то оценки совсем косые, и вид плана другой — видимо, из-за этого)? В любом случае, для тестирования можно SET enable_seqscan = off (ну и/или отключить параллелизм), чтобы план получился такой же. Тем не менее, уже кажется, что что-то сильно не так...
Ок, позвал ещё раз VACUUM ANALYZE. Стало лучше +----------------------------------------------------------------------------------------------------------------------------------------+ |QUERY PLAN | +----------------------------------------------------------------------------------------------------------------------------------------+ |ProjectSet (cost=58.15..6607.32 rows=54380 width=4) (actual time=0.788..293.715 rows=142576 loops=1) | | Output: unnest(attrs) | | -> Bitmap Heap Scan on public.product_v2 p (cost=58.15..6294.63 rows=5438 width=136) (actual time=0.777..14.200 rows=5092 loops=1) | | Output: id, attrs | | Recheck Cond: (p.attrs @> '{678}'::integer[]) | | Heap Blocks: exact=3096 | | -> Bitmap Index Scan on product_v2_attrs_idx (cost=0.00..56.79 rows=5438 width=0) (actual time=0.492..0.494 rows=5092 loops=1)| | Index Cond: (p.attrs @> '{678}'::integer[]) | |Planning Time: 0.068 ms | |Execution Time: 554.375 ms | +----------------------------------------------------------------------------------------------------------------------------------------+
Т.е. получается: ProjectSet (cost=58.15..6607.32 rows=54380 width=4) (actual time=0.788..293.715 rows=142576 loops=1) -- у Вас ProjectSet (cost=55.46..6496.08 rows=50920 width=4) (actual time=4.158..16.461 rows=142576 loops=1) -- у меня Хмм... да не может быть моё [очень] старое "железо" почти в 20 раз лучше Вашего. ;) Может, там какая-то throttled ("зажатая" по ресурсам или с кем-то их делящая так, что ей почти ничего не остаётся) VM, или что-то в этом роде? В общем, мне кажется, что стоит в эту сторону посмотреть для начала.
Мне любопытно -- что на том хосте говорит cat /proc/cpuinfo (Похожэ, L2 кэш мегабайт 8).
Это мой ноут, но там Ryzen 7 4800HS и SSD, а сам pg не в контейнере.
Мне любопытнее было -- что у Ярослава. Но про седьмой мобильный райзен тожэ понял, спасибо.
Кстатида, надо на ноуте тожэ попробовать. (Так-то почти везде у меня -- unnest в этом запросе в 5 раз медленнее, чем выборка. В 20 на ноуте -- понятно. А вот где всего вдвое? Точнее, дажэ примерно той жэ скорости?)
По крайней мере SSD тут ни при чём — тест-то именно в RAM. Казалось бы (но я уже давно не разбираюсь в процессорах!), Ваш должен быть +- то же самое на single thread (но поновее)... может, performance profile (или как там оно называется в Вашем дистрибутиве) не так настроен?
Какой-то Intel Core i3-9100, вроде.
Хотя подождите, что это я (по поводу SSD)... а Вы EXPLAIN (ANALYZE, BUFFERS) показывали, или это я со своими планами перепутал? ;)
Обсуждают сегодня