У меня в настоящее время есть запрос postgresql, который медленный из-за инструкции OR. Из-за этого, по-видимому, он не использует индекс. Снова переписать этот запрос.Замедленный оператор OR в postgresql
Запрос:
EXPLAIN ANALYZE SELECT a0_.id AS id0
FROM advert a0_
INNER JOIN advertcategory a1_
ON a0_.advert_category_id = a1_.id
WHERE a0_.advert_category_id IN (1136)
OR a1_.parent_id IN (1136)
ORDER BY a0_.created_date DESC
LIMIT 15;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.00..27542.49 rows=15 width=12) (actual time=1.658..50.809 rows=15 loops=1)
-> Nested Loop (cost=0.00..1691109.07 rows=921 width=12) (actual time=1.657..50.790 rows=15 loops=1)
-> Index Scan Backward using advert_created_date_idx on advert a0_ (cost=0.00..670300.17 rows=353804 width=16) (actual time=0.013..16.449 rows=12405 loops=1)
-> Index Scan using advertcategory_pkey on advertcategory a1_ (cost=0.00..2.88 rows=1 width=8) (actual time=0.002..0.002 rows=0 loops=12405)
Index Cond: (id = a0_.advert_category_id)
Filter: ((a0_.advert_category_id = 1136) OR (parent_id = 1136))
Rows Removed by Filter: 1
Total runtime: 50.860 ms
Причина медлительности: Filter: ((a0_.advert_category_id = 1136) OR (parent_id = 1136))
Я попытался с помощью INNER JOIN вместо WHERE заявление:
EXPLAIN ANALYZE SELECT a0_.id AS id0
FROM advert a0_
INNER JOIN advertcategory a1_
ON a0_.advert_category_id = a1_.id
AND (a0_.advert_category_id IN (1136)
OR a1_.parent_id IN (1136))
ORDER BY a0_.created_date DESC
LIMIT 15;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.00..27542.49 rows=15 width=12) (actual time=4.667..139.955 rows=15 loops=1)
-> Nested Loop (cost=0.00..1691109.07 rows=921 width=12) (actual time=4.666..139.932 rows=15 loops=1)
-> Index Scan Backward using advert_created_date_idx on advert a0_ (cost=0.00..670300.17 rows=353804 width=16) (actual time=0.019..100.765 rows=12405 loops=1)
-> Index Scan using advertcategory_pkey on advertcategory a1_ (cost=0.00..2.88 rows=1 width=8) (actual time=0.002..0.002 rows=0 loops=12405)
Index Cond: (id = a0_.advert_category_id)
Filter: ((a0_.advert_category_id = 1136) OR (parent_id = 1136))
Rows Removed by Filter: 1
Total runtime: 140.048 ms
Запрос ускоряющий при удалении один из критериев OR. Поэтому я сделал СОЮЗ, чтобы увидеть результаты. Это очень быстро! Но я не считаю это решение:
EXPLAIN ANALYZE
(SELECT a0_.id AS id0
FROM advert a0_
INNER JOIN advertcategory a1_
ON a0_.advert_category_id = a1_.id
WHERE a0_.advert_category_id IN (1136)
ORDER BY a0_.created_date DESC
LIMIT 15)
UNION
(SELECT a0_.id AS id0
FROM advert a0_
INNER JOIN advertcategory a1_
ON a0_.advert_category_id = a1_.id
WHERE a1_.parent_id IN (1136)
ORDER BY a0_.created_date DESC
LIMIT 15);
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=4125.70..4126.00 rows=30 width=12) (actual time=7.945..7.951 rows=15 loops=1)
-> Append (cost=1120.82..4125.63 rows=30 width=12) (actual time=6.811..7.929 rows=15 loops=1)
-> Subquery Scan on "*SELECT* 1" (cost=1120.82..1121.01 rows=15 width=12) (actual time=6.810..6.840 rows=15 loops=1)
-> Limit (cost=1120.82..1120.86 rows=15 width=12) (actual time=6.809..6.825 rows=15 loops=1)
-> Sort (cost=1120.82..1121.56 rows=295 width=12) (actual time=6.807..6.813 rows=15 loops=1)
Sort Key: a0_.created_date
Sort Method: top-N heapsort Memory: 25kB
-> Nested Loop (cost=10.59..1113.59 rows=295 width=12) (actual time=1.151..6.639 rows=220 loops=1)
-> Index Only Scan using advertcategory_pkey on advertcategory a1_ (cost=0.00..8.27 rows=1 width=4) (actual time=1.030..1.033 rows=1 loops=1)
Index Cond: (id = 1136)
Heap Fetches: 1
-> Bitmap Heap Scan on advert a0_ (cost=10.59..1102.37 rows=295 width=16) (actual time=0.099..5.287 rows=220 loops=1)
Recheck Cond: (advert_category_id = 1136)
-> Bitmap Index Scan on idx_54f1f40bd4436821 (cost=0.00..10.51 rows=295 width=0) (actual time=0.073..0.073 rows=220 loops=1)
Index Cond: (advert_category_id = 1136)
-> Subquery Scan on "*SELECT* 2" (cost=3004.43..3004.62 rows=15 width=12) (actual time=1.072..1.072 rows=0 loops=1)
-> Limit (cost=3004.43..3004.47 rows=15 width=12) (actual time=1.071..1.071 rows=0 loops=1)
-> Sort (cost=3004.43..3005.99 rows=626 width=12) (actual time=1.069..1.069 rows=0 loops=1)
Sort Key: a0_.created_date
Sort Method: quicksort Memory: 25kB
-> Nested Loop (cost=22.91..2989.07 rows=626 width=12) (actual time=1.056..1.056 rows=0 loops=1)
-> Index Scan using idx_d84ab8ea727aca70 on advertcategory a1_ (cost=0.00..8.27 rows=1 width=4) (actual time=1.054..1.054 rows=0 loops=1)
Index Cond: (parent_id = 1136)
-> Bitmap Heap Scan on advert a0_ (cost=22.91..2972.27 rows=853 width=16) (never executed)
Recheck Cond: (advert_category_id = a1_.id)
-> Bitmap Index Scan on idx_54f1f40bd4436821 (cost=0.00..22.70 rows=853 width=0) (never executed)
Index Cond: (advert_category_id = a1_.id)
Total runtime: 8.940 ms
(28 rows)
Пробовал реверсирования заявление в:
EXPLAIN ANALYZE SELECT a0_.id AS id0
FROM advert a0_
INNER JOIN advertcategory a1_
ON a0_.advert_category_id = a1_.id
WHERE 1136 IN (a0_.advert_category_id, a1_.parent_id)
ORDER BY a0_.created_date DESC
LIMIT 15;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.00..27542.49 rows=15 width=12) (actual time=1.848..62.461 rows=15 loops=1)
-> Nested Loop (cost=0.00..1691109.07 rows=921 width=12) (actual time=1.847..62.441 rows=15 loops=1)
-> Index Scan Backward using advert_created_date_idx on advert a0_ (cost=0.00..670300.17 rows=353804 width=16) (actual time=0.028..27.316 rows=12405 loops=1)
-> Index Scan using advertcategory_pkey on advertcategory a1_ (cost=0.00..2.88 rows=1 width=8) (actual time=0.002..0.002 rows=0 loops=12405)
Index Cond: (id = a0_.advert_category_id)
Filter: ((1136 = a0_.advert_category_id) OR (1136 = parent_id))
Rows Removed by Filter: 1
Total runtime: 62.506 ms
(8 rows)
Пробовал с помощью EXISTS:
EXPLAIN ANALYZE SELECT a0_.id AS id0
FROM advert a0_
INNER JOIN advertcategory a1_
ON a0_.advert_category_id = a1_.id
WHERE EXISTS(SELECT test.id
FROM advert test
INNER JOIN advertcategory test_cat
ON test_cat.id = test.advert_category_id
WHERE test.id = a0_.id
AND (test.advert_category_id IN (1136)
OR test_cat.parent_id IN (1136)))
ORDER BY a0_.created_date DESC
LIMIT 15;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=45538.18..45538.22 rows=15 width=12) (actual time=524.654..524.673 rows=15 loops=1)
-> Sort (cost=45538.18..45540.48 rows=921 width=12) (actual time=524.651..524.658 rows=15 loops=1)
Sort Key: a0_.created_date
Sort Method: top-N heapsort Memory: 25kB
-> Hash Join (cost=39803.59..45515.58 rows=921 width=12) (actual time=497.362..524.436 rows=220 loops=1)
Hash Cond: (a0_.advert_category_id = a1_.id)
-> Nested Loop (cost=39786.88..45486.21 rows=921 width=16) (actual time=496.748..523.501 rows=220 loops=1)
-> HashAggregate (cost=39786.88..39796.09 rows=921 width=4) (actual time=496.705..496.872 rows=220 loops=1)
-> Hash Join (cost=16.71..39784.58 rows=921 width=4) (actual time=1.210..496.294 rows=220 loops=1)
Hash Cond: (test.advert_category_id = test_cat.id)
Join Filter: ((test.advert_category_id = 1136) OR (test_cat.parent_id = 1136))
Rows Removed by Join Filter: 353584
-> Seq Scan on advert test (cost=0.00..33134.04 rows=353804 width=8) (actual time=0.002..177.953 rows=353804 loops=1)
-> Hash (cost=9.65..9.65 rows=565 width=8) (actual time=0.622..0.622 rows=565 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 22kB
-> Seq Scan on advertcategory test_cat (cost=0.00..9.65 rows=565 width=8) (actual time=0.005..0.327 rows=565 loops=1)
-> Index Scan using advert_pkey on advert a0_ (cost=0.00..6.17 rows=1 width=16) (actual time=0.117..0.118 rows=1 loops=220)
Index Cond: (id = test.id)
-> Hash (cost=9.65..9.65 rows=565 width=4) (actual time=0.604..0.604 rows=565 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 20kB
-> Seq Scan on advertcategory a1_ (cost=0.00..9.65 rows=565 width=4) (actual time=0.010..0.285 rows=565 loops=1)
Total runtime: 524.797 ms
Advert стол (усеченную):
353804 rows
Table "public.advert"
Column | Type | Modifiers | Storage | Stats target | Description
-----------------------------+--------------------------------+-----------------------------------------------------+----------+--------------+-------------
id | integer | not null default nextval('advert_id_seq'::regclass) | plain | |
advert_category_id | integer | not null | plain | |
Indexes:
"idx_54f1f40bd4436821" btree (advert_category_id)
"advert_created_date_idx" btree (created_date)
Foreign-key constraints:
"fk_54f1f40bd4436821" FOREIGN KEY (advert_category_id) REFERENCES advertcategory(id) ON DELETE RESTRICT
Has OIDs: no
Вкладка "Категория" ле (урезанный):
565 rows
Table "public.advertcategory"
Column | Type | Modifiers
-----------+---------+-------------------------------------------------------------
id | integer | not null default nextval('advertcategory_id_seq'::regclass)
parent_id | integer |
active | boolean | not null
system | boolean | not null
Indexes:
"advertcategory_pkey" PRIMARY KEY, btree (id)
"idx_d84ab8ea727aca70" btree (parent_id)
Foreign-key constraints:
"fk_d84ab8ea727aca70" FOREIGN KEY (parent_id) REFERENCES advertcategory(id) ON DELETE RESTRICT
Коротких конфигурации сервера:
version
--------------------------------------------------------------------------------------------------------------
PostgreSQL 9.2.4 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3), 64-bit
name | current_setting | source
----------------------------+--------------------+----------------------
shared_buffers | 1800MB | configuration file
work_mem | 4MB | configuration file
Как вы можете видеть, ни один из правильных решений не дают улучшение скорости. Только решение UNION для разделения оператора OR повышает производительность. Но я не могу использовать это, потому что этот запрос используется через мою структуру ORM с большим количеством других параметров фильтра. Плюс, если я могу это сделать, почему оптимизатор этого не делает? Это действительно простая оптимизация.
Любые намеки на это? Решение этой маленькой проблемы получило бы высокую оценку!
Оптимизатор не сделает этого, потому что это очень очень редко, что профсоюз - это оптимизация, и правила, чтобы создать союз, как один, далеки от общего. Думали ли вы, что для каждого из них есть один метод ORM, а затем добавьте метод, чтобы получить лучшую клиентскую сторону 15. Бит непослушный, но с учетом предела остается небольшим, а критерии просты, а не крупными. –
Возможно. Но я не хочу перестраивать свой репозиторий в UNION для каждого крошечного критерия. Поэтому я мог бы назвать это дважды, разделив аргументы. Тхо ... Мне все равно нужно отсортировать приложение прямо? – mauserrifle
Да, когда вам это было нужно. Вы получите два набора записей (или добавьте один к другому, а затем передадите его во внутреннюю сортировку и отрежьте верхнюю часть 15. Непослушный, но выполнимый, учитывая объем данных, которые вы возвращаете. Другие варианты были бы навязчивыми все это на вашем ORM и посмотреть, что он с ним делает, или переделать свою модель в соответствии с такими потребностями, подумал, что это может вызвать проблемы в других областях. –