Почему этот запрос pg такой медленный?Как я могу сделать это быстрее? - PullRequest
2 голосов
/ 28 сентября 2019

Это запрос:

(SELECT * 
FROM url 
WHERE domain = 'youtube.com' 
  AND timestamp > NOW() - INTERVAL '24 hours' 
ORDER BY likes DESC LIMIT 10) 
UNION 
(SELECT * 
FROM url 
WHERE domain = 'twitter.com' 
  AND timestamp > NOW() - INTERVAL '24 hours' 
ORDER BY likes DESC LIMIT 10) 
UNION 
(SELECT * 
FROM url 
WHERE domain = 'reddit.com' 
  AND timestamp > NOW() - INTERVAL '24 hours' 
ORDER BY likes DESC LIMIT 10) 
ORDER BY timestamp DESC

Это EXPLAIN ANALYZE.

Sort  (cost=20460.17..20460.25 rows=30 width=497) (actual time=5161.013..5161.015 rows=30 loops=1)
  Sort Key: url."timestamp" DESC
  Sort Method: quicksort  Memory: 53kB
  ->  HashAggregate  (cost=20459.14..20459.44 rows=30 width=497) (actual time=5160.709..5160.738 rows=30 loops=1)
        Group Key: url.url, url.domain, url.title, url.views, url.likes, url.dislikes, url.comments, url.shares, url.links_to_url, url."user", url.thumbnail_url, url.is_collection, url.image_url, url.video_url, url.audio_url, url.width, url.height, url.body, url.source, url."timestamp", url.created_at, url.updated_at, url.duration_seconds, url.tags, url.channel
        ->  Append  (cost=0.43..20457.26 rows=30 width=497) (actual time=0.514..5160.073 rows=30 loops=1)
              ->  Limit  (cost=0.43..18150.71 rows=10 width=1177) (actual time=0.513..28.599 rows=10 loops=1)
                    ->  Index Scan Backward using "url-likes-index" on url  (cost=0.43..816763.00 rows=450 width=1177) (actual time=0.511..28.594 rows=10 loops=1)
                          Filter: (((domain)::text = 'youtube.com'::text) AND ("timestamp" > (now() - '24:00:00'::interval)))
                          Rows Removed by Filter: 11106
              ->  Limit  (cost=0.43..859.82 rows=10 width=1177) (actual time=2330.390..5033.214 rows=10 loops=1)
                    ->  Index Scan Backward using "url-likes-index" on url url_1  (cost=0.43..816763.00 rows=9504 width=1177) (actual time=2330.388..5033.200 rows=10 loops=1)
                          Filter: (((domain)::text = 'twitter.com'::text) AND ("timestamp" > (now() - '24:00:00'::interval)))
                          Rows Removed by Filter: 1667422
              ->  Limit  (cost=0.43..1446.28 rows=10 width=1177) (actual time=64.748..98.228 rows=10 loops=1)
                    ->  Index Scan Backward using "url-likes-index" on url url_2  (cost=0.43..816763.00 rows=5649 width=1177) (actual time=64.745..98.220 rows=10 loops=1)
                          Filter: (((domain)::text = 'reddit.com'::text) AND ("timestamp" > (now() - '24:00:00'::interval)))
                          Rows Removed by Filter: 26739
Planning Time: 3.006 ms
Execution Time: 5162.201 ms

И если вы хотите запустить его самостоятельно, перейдите к этомуссылка .

Я вижу, что фильтруется миллион строк в Твиттере, но я не уверен, как этого избежать.У меня есть индекс timestamp, и я надеялся, что он будет использован вместо сортировки по likes и сканирования всего этого.Значит ли это, что мне нужен составной индекс?Есть ли способ заставить планировщик использовать оба индекса вместо создания другого?

ps Я думаю, что я обманываю с первичным ключом, являющимся URL.Это делает индексы без необходимости больше.

Ответы [ 2 ]

2 голосов
/ 28 сентября 2019

PostgreSQL пытается использовать индекс на likes, чтобы избежать сортировки, чтобы получить первые 10 результатов, но ему нужно отбросить много строк, чтобы туда попасть.

Возможно, план выполнения является лучшим,возможно, нет.

Выполните следующие действия:

  1. Запустите ANALYZE на своей таблице и посмотрите, решит ли это проблему.

  2. Если нет, создайте индекс для (domain, timestamp) (в таком порядке!) И посмотрите, не улучшится ли это.

  3. Если этого недостаточно, либо

    • перетащите индекс на likes (если можете)

    или

    • измените ORDER BY likes на ORDER BY likes + 0.

Если все, что не делает его лучше, то ваш исходный план запросов был лучшим, и все, что вы можете сделать, это использовать больше оперативной памяти в надежде, что больше ваших данных будет в кеше.

1 голос
/ 28 сентября 2019

Я бы предложил написать запрос следующим образом:

SELECT ufiltered.*
FROM (SELECT url.*,
            ROW_NUMBER() OVER (PARTITION BY domain ORDER BY likes DESC) AS seqnum
      FROM url 
      WHERE domain IN ('youtube.com', 'twitter.com', 'reddit.com') AND
            timestamp > NOW() - INTERVAL '24 hours'
    ) AS ufiltered
WHERE seqnum <= 10
ORDER BY timestamp DESC

Для этого я бы порекомендовал индекс для url(timestamp, domain, likes).

...