인덱스 스캔 대신 PostgreSQL 순차 스캔 왜?


12

안녕하십니까. PostgreSQL 데이터베이스 쿼리에 문제가 있으며 누군가 도울 수 있는지 궁금합니다. 일부 시나리오에서는 내 쿼리는 두 개의 테이블을 조인에 사용되는 제작 한 것을 인덱스 무시하는 것 data등을 data_area. 이 경우 순차적 스캔을 사용하므로 쿼리 속도가 훨씬 느려집니다.

순차적 스캔 (~ 5 분)

Unique  (cost=15368261.82..15369053.96 rows=200 width=1942) (actual time=301266.832..301346.936 rows=153812 loops=1)
   CTE data
     ->  Bitmap Heap Scan on data  (cost=6086.77..610089.54 rows=321976 width=297) (actual time=26.286..197.625 rows=335130 loops=1)
           Recheck Cond: (datasetid = 1)
           Filter: ((readingdatetime >= '1920-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2013-03-11 00:00:00'::timestamp without time zone) AND (depth >= 0::double precision) AND (depth <= 99999::double precision))
           ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..6006.27 rows=324789 width=0) (actual time=25.462..25.462 rows=335130 loops=1)
                 Index Cond: (datasetid = 1)
   ->  Sort  (cost=15368261.82..15368657.89 rows=158427 width=1942) (actual time=301266.829..301287.110 rows=155194 loops=1)
         Sort Key: data.id
         Sort Method: quicksort  Memory: 81999kB
         ->  Hash Left Join  (cost=15174943.29..15354578.91 rows=158427 width=1942) (actual time=300068.588..301052.832 rows=155194 loops=1)
               Hash Cond: (data_area.area_id = area.id)
               ->  Hash Join  (cost=15174792.93..15351854.12 rows=158427 width=684) (actual time=300066.288..300971.644 rows=155194 loops=1)
                     Hash Cond: (data.id = data_area.data_id)
                     ->  CTE Scan on data  (cost=0.00..6439.52 rows=321976 width=676) (actual time=26.290..313.842 rows=335130 loops=1)
                     ->  Hash  (cost=14857017.62..14857017.62 rows=25422025 width=8) (actual time=300028.260..300028.260 rows=26709939 loops=1)
                           Buckets: 4194304  Batches: 1  Memory Usage: 1043357kB
                           ->  Seq Scan on data_area  (cost=0.00..14857017.62 rows=25422025 width=8) (actual time=182921.056..291687.996 rows=26709939 loops=1)
                                 Filter: (area_id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
               ->  Hash  (cost=108.49..108.49 rows=3349 width=1258) (actual time=2.256..2.256 rows=3349 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 584kB
                     ->  Seq Scan on area  (cost=0.00..108.49 rows=3349 width=1258) (actual time=0.007..0.666 rows=3349 loops=1)
 Total runtime: 301493.379 ms

인덱스 스캔 (~ 3 초) ( Explain.depesz.com )

Unique  (cost=17352256.47..17353067.50 rows=200 width=1942) (actual time=3603.303..3681.619 rows=153812 loops=1)
   CTE data
     ->  Bitmap Heap Scan on data  (cost=6284.60..619979.56 rows=332340 width=297) (actual time=26.201..262.314 rows=335130 loops=1)
           Recheck Cond: (datasetid = 1)
           Filter: ((readingdatetime >= '1920-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2013-03-11 00:00:00'::timestamp without time zone) AND (depth >= 0::double precision) AND (depth <= 99999::double precision))
           ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..6201.51 rows=335354 width=0) (actual time=25.381..25.381 rows=335130 loops=1)
                 Index Cond: (datasetid = 1)
   ->  Sort  (cost=17352256.47..17352661.98 rows=162206 width=1942) (actual time=3603.302..3623.113 rows=155194 loops=1)
         Sort Key: data.id
         Sort Method: quicksort  Memory: 81999kB
         ->  Hash Left Join  (cost=1296.08..17338219.59 rows=162206 width=1942) (actual time=29.980..3375.921 rows=155194 loops=1)
               Hash Cond: (data_area.area_id = area.id)
               ->  Nested Loop  (cost=0.00..17334287.66 rows=162206 width=684) (actual time=26.903..3268.674 rows=155194 loops=1)
                     ->  CTE Scan on data  (cost=0.00..6646.80 rows=332340 width=676) (actual time=26.205..421.858 rows=335130 loops=1)
                     ->  Index Scan using data_area_pkey on data_area  (cost=0.00..52.13 rows=1 width=8) (actual time=0.006..0.008 rows=0 loops=335130)
                           Index Cond: (data_id = data.id)
                           Filter: (area_id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
               ->  Hash  (cost=1254.22..1254.22 rows=3349 width=1258) (actual time=3.057..3.057 rows=3349 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 584kB
                     ->  Index Scan using area_primary_key on area  (cost=0.00..1254.22 rows=3349 width=1258) (actual time=0.012..1.429 rows=3349 loops=1)
 Total runtime: 3706.630 ms

테이블 구조

이것은 테이블의 테이블 구조입니다 data_area. 필요한 경우 다른 테이블을 제공 할 수 있습니다.

CREATE TABLE data_area
(
  data_id integer NOT NULL,
  area_id integer NOT NULL,
  CONSTRAINT data_area_pkey PRIMARY KEY (data_id , area_id ),
  CONSTRAINT data_area_area_id_fk FOREIGN KEY (area_id)
      REFERENCES area (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT data_area_data_id_fk FOREIGN KEY (data_id)
      REFERENCES data (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
);

질문

WITH data AS (
    SELECT * 
    FROM data 
    WHERE 
        datasetid IN (1) 
        AND (readingdatetime BETWEEN '1920-01-01' AND '2013-03-11') 
        AND depth BETWEEN 0 AND 99999
)
SELECT * 
FROM ( 
    SELECT DISTINCT ON (data.id) data.id, * 
    FROM 
        data, 
        data_area 
        LEFT JOIN area ON area_id = area.id 
    WHERE 
        data_id = data.id 
        AND area_id IN (28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11) 
) as s;

153812행을 반환 합니다. 나요 set enable_seqscan= false;해제 순차 검색과 인덱스 결과를 얻을.

ANALYSE데이터베이스에서 작업을 시도하고 쿼리에 사용 된 열에 수집 된 통계를 늘리려 고 했지만 아무것도 도움이되지 않는 것 같습니다.

누구든지 이것에 퍼지고 빛을 발하거나 내가 시도해야 할 다른 것을 제안 할 수 있습니까?


그것은 도움이 될 나에게 당신이 그 실행 계획의 각을 생성 쿼리를 포함합니다.
마이크 Sherrill '고양이 리콜

예상 행 수와 실제 행 수에서 2 배의 차이는 무엇입니까? 내가 올바르게 읽고 있습니까?
Mike Sherrill 'Cat

@Catcall 쿼리를 추가했습니다 (행사를 처리 할 수있는 기본적인 기능). 예상 행을 참조하면 200이 실제로 153812를 반환합니까?
Mark Davidson

2
예, 200 대 150k는 한 눈에 이상하게 보입니다. 왼쪽 조인을 카티 전 곱 ( FROM data, data_area) 과 혼합해야하는 강력한 이유가 있습니까? 언뜻보기에 ORDER BY 절없이 DISTINCT ON을 사용하는 것은 나쁜 생각 인 것 같습니다.
Mike Sherrill 'Cat

Explain.depesz.com/s/Uzin 이 유익 할 수 있습니다.
Craig Ringer

답변:


8

이 줄을 주목하십시오 :

->  Index Scan using data_area_pkey on data_area  (cost=0.00..52.13 rows=1 width=8) 
    (actual time=0.006..0.008 rows=0 loops=335130)

루프를 고려하여 총 비용을 계산하면됩니다 52.3 * 335130 = 17527299. seq_scan대안의 경우 14857017.62보다 큽니다 . 이것이 인덱스를 사용하지 않는 이유입니다.

따라서 옵티마이 저는 인덱스 스캔 비용을 과대 평가하고 있습니다. 클러스터 된 인덱스 또는로드 방법으로 인해 인덱스에서 데이터가 정렬되었거나 캐시 메모리가 많고 빠른 디스크가 충분하다고 생각합니다. 따라서 임의의 I / O가 거의 발생하지 않습니다.

또한 확인해야합니다 correlation에서 pg_stats인덱스 비용을 계산할 때 클러스터링을 평가하고, 마지막으로 변경 시도 옵티 마이저에 의해 사용되는, random_page_costcpu_index_tuple_cost시스템을 일치를.


내가 빠진 것이 아니라면 @jop가 52.13아닌 것으로 생각합니다 52.3. 17470326.9 (여전히 seq_scan보다 큼)
BotNet

2

CTE는 실제로 다른 WHERE조건 을 수행하지 않고 몇 가지 조건 을 '아웃소싱'합니다 WHERE TRUE. CTE는 일반적으로 최적화 펜스 뒤에 있기 때문에 (자체적으로 최적화됨을 의미) 특정 쿼리에 많은 도움이 될 수 있습니다. 그러나이 경우 정확한 반대 효과를 기대합니다.

내가 시도하는 것은 가능한 한 간단하게 쿼리를 다시 작성하는 것입니다.

SELECT d.id, * 
FROM 
    data d 
    JOIN data_area da ON da.data_id = d.id
    LEFT JOIN area a ON da.area_id = a.id 
WHERE 
    d.datasetid IN (1) 
    AND da.area_id IN (28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11) 
    AND (readingdatetime BETWEEN '1920-01-01' AND '2013-03-11') -- this and the next condition don't do anything, I think
    AND depth BETWEEN 0 AND 99999
;

색인 사용 여부를 확인하십시오. 모든 출력 열이 필요하지는 않지만 여전히 가능합니다 (접합 테이블의 두 열은 불필요합니다).

다시보고하고 사용중인 PostgreSQL 버전을 알려주십시오.


귀하의 제안에 감사드립니다. 귀하의 게시물에 대한 답변이 지연되어 죄송합니다. 다른 프로젝트를 진행하고 있습니다. 귀하의 제안은 실제로 쿼리가 모든 쿼리에 대해 인덱스를 안정적으로 사용하는 것처럼 보이지만 여전히 기대했던 성능을 얻지 못하고 있음을 의미합니다. Explain.depesz.com/s/1yu에 더 많은 데이터가있는 쿼리에 대한 분석을 수행 한 결과 INDEX 스캔에 95 %의 시간이 소요됩니다.
Mark Davidson

내가 버전 9.1.4를 사용하고 있음을 언급하는 것을 잊었다
Mark Davidson

기본적으로 인덱스 스캔은 매우 빠르지 만 문제는 수백만 번 반복된다는 것입니다. SET enable_nestloop=off쿼리를 실행하기 전에 무엇을 얻 습니까?
dezso

-1

추종자들에게 비슷한 문제가있었습니다.

select * from table where bigint_column between x and y and mod(bigint_column, 10000) == z

문제는 내 xint와 y 사이의 bigint_column에 인덱스가 있지만 쿼리는 기본적으로 해당 테이블의 "모든 행"이므로 인덱스를 사용하지 않았기 때문에 [어쨌든 전체 테이블을 스캔해야 했으므로] seq_scan 순차 스캔을 수행했습니다. 나를위한 해결책은 방정식의 "mod"측에 대한 새로운 색인을 작성 하여 표현식에서 사용할 수 있도록하는 것 입니다.


downvoters :)에 대한 의견을 남길 수 있습니다 :)
rogerdpack
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.