장시간 실행되는 Sidekiq 작업

실제로 업데이트되는 랭킹이 몇 개든 상관없이, 전체 게시글 테이블에 걸쳐 모든 주제에 대한 랭킹을 계산하기 위해 서브쿼리를 실행해야 합니다.

이 서브쿼리를 실행하는 데 약 40초가 걸리며, WHERE 절에 도달하기도 전에 조인을 위해 반복 실행되어야 하는 중첩 루프 안에 있습니다. 이것이 쿼리 비용을 폭증시키는 원인으로 보입니다.

SELECT id, percent_rank()                                                     
    OVER (PARTITION BY topic_id ORDER BY SCORE DESC) as percent_rank 
FROM posts
1개의 좋아요

코드를 살펴본 후 이제 이해했습니다. 이 쿼리는 랭크 업데이트가 필요한 모든 게시물을 처리할 때까지 반복적으로 실행되므로, SQL 프로세스를 관찰하던 중 한 실행이 끝나고 다른 실행이 시작되는 과정을 시간 단위로 여러 번 확인했습니다.

그러나 LIMIT가 쿼리의 복잡성에 실제로는 그렇게 큰 도움이 되지 않는 것 같습니다. 사실, limit을 200000으로 증가시키면 쿼리 플랜이 상당히 개선되는 것을 발견했습니다.
20000일 때 비용이 거의 2억에 달하는 거대한 nested loop를 볼 수 있습니다:

-> Nested Loop (cost=6033694.44..190270554.32 rows=26680560 width=16)

limit을 단순히 200000으로만 증가시키면 훨씬 깔끔해 보이는 다음과 같은 쿼리 플랜이 생성됩니다:

                                                            QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
 Update on posts  (cost=7334394.89..8873863.82 rows=200000 width=833)
   ->  Nested Loop  (cost=7334394.89..8873863.82 rows=200000 width=833)
         ->  Subquery Scan on x  (cost=7334394.45..7370147.82 rows=200000 width=48)
               ->  Limit  (cost=7334394.45..7368147.82 rows=200000 width=12)
                     ->  Hash Join  (cost=7334394.45..11837188.36 rows=26680560 width=12)
                           Hash Cond: (posts_1.id = y.id)
                           Join Filter: ((posts_1.percent_rank IS NULL) OR (y.percent_rank <> posts_1.percent_rank))
                           ->  Hash Join  (cost=35403.20..2845221.73 rows=26703700 width=12)
                                 Hash Cond: (posts_1.topic_id = topics.id)
                                 ->  Seq Scan on posts posts_1  (cost=0.00..2739721.00 rows=26703700 width=16)
                                 ->  Hash  (cost=23918.11..23918.11 rows=918807 width=4)
                                       ->  Index Only Scan using topics_pkey on topics  (cost=0.42..23918.11 rows=918807 width=4)
                           ->  Hash  (cost=6834805.00..6834805.00 rows=26703700 width=12)
                                 ->  Subquery Scan on y  (cost=6033694.00..6834805.00 rows=26703700 width=12)
                                       ->  WindowAgg  (cost=6033694.00..6567768.00 rows=26703700 width=24)
                                             ->  Sort  (cost=6033694.00..6100453.25 rows=26703700 width=16)
                                                   Sort Key: posts_2.topic_id, posts_2.score DESC
                                                   ->  Seq Scan on posts posts_2  (cost=0.00..2739721.00 rows=26703700 width=16)
         ->  Index Scan using posts_pkey on posts  (cost=0.44..7.52 rows=1 width=789)
               Index Cond: (id = x.id)
 JIT:
   Functions: 30
   Options: Inlining true, Optimization true, Expressions true, Deforming true
(23 rows)

무엇이 플랜 전환을 일으키는지 궁금합니다. 백로그가 소진되는 동안, 우리가 수행한 것과 같은 대규모 임포트 이후에는 이 값을 최소한 증가시키는 것이 가치가 있을 수 있습니다.

2개의 좋아요