Postgres seq 스캔에 대한 시작 시간 및 다양한 계획 해석

Nov 28 2020

최근 질문을 하면서 EXPLAIN ANALYZE 출력에 신비한 시작 시간 구성 요소가 나타났습니다. 나는 더 놀고 있었고 정규식 WHERE절을 제거하면 시작 시간이 거의 0으로 떨어지는 것을 발견했습니다 .

다음 bash 스크립트를 테스트로 실행했습니다.

for i in $(seq 1 10) do if (( $RANDOM % 2 == 0 ))
    then
        echo "Doing plain count"
        psql -e -c "EXPLAIN ANALYZE SELECT count(*) FROM ui_events_v2"
    else
        echo "Doing regex count"
        psql -e -c "EXPLAIN ANALYZE SELECT count(*) FROM ui_events_v2 WHERE page ~ 'foo'"
    fi
done

첫 번째 쿼리는 ~ 3 천만 개의 수를 반환하고 두 번째 쿼리는 7 개의 행만 계산합니다. 최소한의 다른 작업으로 RDS의 PG 12.3 읽기 전용 복제본에서 실행됩니다. 두 버전 모두 예상대로 거의 같은 시간이 걸립니다. 다음은 다음으로 필터링 된 일부 출력입니다 grep.

Doing plain count
               ->  Parallel Seq Scan on ui_events_v2  (cost=0.00..3060374.07 rows=12632507 width=0) (actual time=0.086..38622.215 rows=10114306 loops=3)
Doing regex count
               ->  Parallel Seq Scan on ui_events_v2  (cost=0.00..3091955.34 rows=897 width=0) (actual time=16856.679..41398.062 rows=2 loops=3)
Doing plain count
               ->  Parallel Seq Scan on ui_events_v2  (cost=0.00..3060374.07 rows=12632507 width=0) (actual time=0.162..39454.499 rows=10114306 loops=3)
Doing plain count
               ->  Parallel Seq Scan on ui_events_v2  (cost=0.00..3060374.07 rows=12632507 width=0) (actual time=0.036..39213.171 rows=10114306 loops=3)
Doing regex count
               ->  Parallel Seq Scan on ui_events_v2  (cost=0.00..3091955.34 rows=897 width=0) (actual time=12711.308..40015.734 rows=2 loops=3)
Doing plain count
               ->  Parallel Seq Scan on ui_events_v2  (cost=0.00..3060374.07 rows=12632507 width=0) (actual time=0.244..39277.683 rows=10114306 loops=3)
Doing regex count
^CCancel request sent

따라서 몇 가지 질문이 있습니다.

  1. 정규식 스캔에서 "실제 시간"의이 시작 구성 요소에 들어가는 것은 무엇이며 왜 그렇게 더 큰가요? (10-20 초 vs 0-1 초)

  2. "비용"과 "시간"이 비교 가능한 단위는 아니지만 플래너는 모든 경우에 시작 비용이 0이어야한다고 생각하는 것 같습니다. 속고 있습니까?

  3. 전략이 다른 이유는 무엇입니까? 두 계획 모두를 언급 Partial Aggregate하지만 정규식 쿼리는 실제 행이라고 말하지만 2일반 버전은 실제 행이 ~ 천만이라고 말합니다 (이것은 작업자 2 명과 리더 1 명 사이의 평균이며 ~ 3 천만 명입니다). 이것을 직접 구현해야한다면 count(*)행을 병합하고 계산하는 대신 여러 작업 의 결과를 더할 것입니다 . 계획이 정확히 얼마나 정확히 수행하는지 표시합니까?

그래서 나는 아무것도 숨기지 않으며, 아래는 각각에 대한 쿼리 계획의 전체 버전입니다.


EXPLAIN ANALYZE SELECT count(*) FROM ui_events_v2
                                                                       QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=3093171.59..3093171.60 rows=1 width=8) (actual time=39156.499..39156.499 rows=1 loops=1)
   ->  Gather  (cost=3093171.37..3093171.58 rows=2 width=8) (actual time=39156.356..39157.850 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=3092171.37..3092171.38 rows=1 width=8) (actual time=39154.405..39154.406 rows=1 loops=3)
               ->  Parallel Seq Scan on ui_events_v2  (cost=0.00..3060587.90 rows=12633390 width=0) (actual time=0.033..38413.690 rows=10115030 loops=3)
 Planning Time: 7.968 ms
 Execution Time: 39157.942 ms
(8 rows)


EXPLAIN ANALYZE SELECT count(*) FROM ui_events_v2 WHERE page ~ 'foo'
                                                                   QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=3093173.83..3093173.84 rows=1 width=8) (actual time=39908.495..39908.495 rows=1 loops=1)
   ->  Gather  (cost=3093173.61..3093173.82 rows=2 width=8) (actual time=39908.408..39909.848 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=3092173.61..3092173.62 rows=1 width=8) (actual time=39906.317..39906.318 rows=1 loops=3)
               ->  Parallel Seq Scan on ui_events_v2  (cost=0.00..3092171.37 rows=897 width=0) (actual time=17250.058..39906.308 rows=2 loops=3)
                     Filter: (page ~ 'foo'::text)
                     Rows Removed by Filter: 10115028
 Planning Time: 0.803 ms
 Execution Time: 39909.921 ms
(10 rows)

답변

3 LaurenzAlbe Nov 28 2020 at 03:30
  1. 비밀은 다음과 Rows Removed by Filter: 10115028같습니다.

    순차 스캔은 첫 번째 결과 행을 찾아 리턴하는 데 17 초가 걸립니다.

  2. 옵티마이 저는 첫 번째 행이 필터를 통과 할 때까지 걸리는 시간을 모릅니다. 계획의 품질에 아무런 차이가 없으므로 시작 비용을 0으로 설정합니다.

  3. 두 계획 모두 동일하게 작동합니다. 세 프로세스 각각이 테이블의 1/3을 스캔하고 행을 계산합니다 ( Partial Aggregate1 행을 반환 함). 첫 번째 경우에는 각 작업자가 1,000 만 개의 행을 계산합니다.

    이 세 숫자는 Finalize Aggregate무대 에서 모아서 합산됩니다 .