0

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

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

Это один быстро:

SELECT 
    "posts".* 
FROM "posts" 
WHERE "posts"."source_id" IN (29949, 29952, 29950, 33642, 33626, 33627, 33625) 
AND "posts"."deleted_at" IS NULL 
AND "posts"."rejected_at" IS NULL 
ORDER BY POSITION ASC, external_created_at DESC; 
LIMIT 100 
OFFSET 0 

Это один медленно:

SELECT 
    "posts".* 
FROM "posts" 
WHERE "posts"."source_id" IN (29949, 29952, 29950, 33642, 33626, 33627, 33625) 
AND "posts"."deleted_at" IS NULL 
AND "posts"."rejected_at" IS NULL 
ORDER BY POSITION ASC, external_created_at DESC; 
LIMIT 5 
OFFSET 0 

Единственное различие здесь есть предел.

Самая странная часть является то, что очень похожий запрос на # 2 быстро:

SELECT 
    "posts".* 
FROM "posts" 
WHERE "posts"."source_id" IN (5868, 5867) 
AND "posts"."deleted_at" IS NULL 
AND "posts"."rejected_at" IS NULL 
ORDER BY POSITION ASC, external_created_at DESC; 
LIMIT 100 
OFFSET 0 

Который просто смотрит через меньший диапазон source_ids

Вот планы запросов для всех трех:

EXPLAIN ANALYZE SELECT "posts".* FROM "posts" WHERE "posts"."source_id" IN (29949, 29952, 29950, 33642, 33626, 33627, 33625) AND "posts"."deleted_at" IS NULL AND "posts"."rejected_at" IS NULL ORDER BY POSITION ASC, external_created_at DESC LIMIT 100 OFFSET 0; 
---------------------------------------------------------------------------------------------------------------------------------------------------------- 
Limit (cost=36900.88..36901.13 rows=100 width=1051) (actual time=104.564..104.570 rows=28 loops=1) 
    -> Sort (cost=36900.88..36926.01 rows=10052 width=1051) (actual time=104.559..104.563 rows=28 loops=1) 
     Sort Key: "position", external_created_at 
     Sort Method: quicksort Memory: 53kB 
     -> Index Scan using index_posts_on_source_id on posts (cost=0.44..36516.70 rows=10052 width=1051) (actual time=9.724..102.885 rows=28 loops=1) 
       Index Cond: (source_id = ANY ('{29949,29952,29950,33642,33626,33627,33625}'::integer[])) 
       Filter: ((deleted_at IS NULL) AND (rejected_at IS NULL)) 
       Rows Removed by Filter: 1737 
Total runtime: 105.774 ms 


EXPLAIN ANALYZE SELECT "posts".* FROM "posts" WHERE "posts"."source_id" IN (29949, 29952, 29950, 33642, 33626, 33627, 33625) AND "posts"."deleted_at" IS NULL AND "posts"."rejected_at" IS NULL ORDER BY POSITION ASC, external_created_at DESC LIMIT 5 OFFSET 0; 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 
Limit (cost=0.56..18788.72 rows=5 width=1051) (actual time=79611.044..314266.666 rows=5 loops=1) 
    -> Index Scan using index_posts_on_position_and_external_created_at on posts (cost=0.56..37771717.36 rows=10052 width=1051) (actual time=79610.677..314266.292 rows=5 loops=1) 
     Filter: ((deleted_at IS NULL) AND (rejected_at IS NULL) AND (source_id = ANY ('{29949,29952,29950,33642,33626,33627,33625}'::integer[]))) 
     Rows Removed by Filter: 3665332 
Total runtime: 314269.266 ms 


EXPLAIN ANALYZE SELECT "posts".* FROM "posts" WHERE "posts"."source_id" IN (5868, 5867) AND "posts"."deleted_at" IS NULL AND "posts"."rejected_at" IS NULL ORDER BY POSITION ASC, external_created_at DESC LIMIT 100 OFFSET 0; 
----------------------------------------------------------------------------------------------------------------------------------------------------------- 
Limit (cost=10587.37..10587.62 rows=100 width=1051) (actual time=1017.476..1017.498 rows=100 loops=1) 
    -> Sort (cost=10587.37..10594.55 rows=2872 width=1051) (actual time=1017.474..1017.483 rows=100 loops=1) 
     Sort Key: "position", external_created_at 
     Sort Method: top-N heapsort Memory: 112kB 
     -> Index Scan using index_posts_on_source_id on posts (cost=0.44..10477.60 rows=2872 width=1051) (actual time=2.823..999.417 rows=4334 loops=1) 
       Index Cond: (source_id = ANY ('{5868,5867}'::integer[])) 
       Filter: ((deleted_at IS NULL) AND (rejected_at IS NULL)) 
       Rows Removed by Filter: 39 
Total runtime: 1017.669 ms 

Вот мои определения индекса:

"posts_pkey" PRIMARY KEY, btree (id) 
"index_posts_on_deleted_at" btree (deleted_at) 
"index_posts_on_external_created_at" btree (external_created_at) 
"index_posts_on_external_id" btree (external_id) 
"index_posts_on_position" btree ("position") 
"index_posts_on_position_and_external_created_at" btree ("position", external_created_at DESC) 
"index_posts_on_rejected_at" btree (rejected_at) 
"index_posts_on_source_id" btree (source_id) 

Я бегу версия Postgres: 9.3.4

Почему медленно один с помощью Index Scan using index_posts_on_position_and_external_created_at on posts, когда другие два используют Index Scan using index_posts_on_source_id on posts? И как я могу это исправить?

+1

Должно быть очевидно, чтобы включать определения таблиц и индексов, а также вашу версию Postgres. Пожалуйста, [рассмотрите инструкцию здесь] (http://stackoverflow.com/tags/postgresql-performance/info) и добавьте необходимую информацию. Во-первых, статистика и/или сметы расходов, вероятно, уходят. рассмотрите: http://stackoverflow.com/a/8229000/939860 –

ответ

1

Немного позднего ответа, в случае, если у вас все еще есть эта проблема, почему бы не просто отказаться index_posts_on_position_and_external_created_at? Как вы сказали, проблема возникает, когда этот конкретный индекс используется планировщиком запросов.

У вас уже есть эти два индекса:

"index_posts_on_external_created_at" btree (external_created_at) 
"index_posts_on_position" btree ("position") 

Эти два делает index_posts_on_position_and_external_created_at в значительной степени избыточным, поскольку PostgreSQL может использовать более одного индекс по данному запросу. Если вас беспокоит производительность сортировки, вы можете добавить заказ на index_posts_on_external_created_at