큰 테이블에서 느린 인덱스 스캔


12

PostgreSQL 9.2를 사용하면 비교적 큰 테이블 (2 억 + 백만 행)에서 느린 쿼리로 인해 문제가 발생합니다. 나는 미친 것을 시도하지 않고 역사적인 가치를 추가합니다. 아래는 쿼리 및 쿼리 계획 출력입니다.

내 테이블 레이아웃 :

                                   Table "public.energy_energyentry"
  Column   |           Type           |                            Modifiers
-----------+--------------------------+-----------------------------------------------------------------
 id        | integer                  | not null default nextval('energy_energyentry_id_seq'::regclass)
 prop_id   | integer                  | not null
 timestamp | timestamp with time zone | not null
 value     | double precision         | not null
Indexes:
    "energy_energyentry_pkey" PRIMARY KEY, btree (id)
    "energy_energyentry_prop_id" btree (prop_id)
    "energy_energyentry_prop_id_timestamp_idx" btree (prop_id, "timestamp")
Foreign-key constraints:
    "energy_energyentry_prop_id_fkey" FOREIGN KEY (prop_id) REFERENCES gateway_peripheralproperty(id) DEFERRABLE INITIALLY DEFERRED

데이터 범위는 2012 년 1 월 1 일부터 현재까지이며 새로운 데이터가 지속적으로 추가됩니다. prop_id외래 키 에는 약 2.2k의 고유 한 값이 있으며 균등하게 분배됩니다.

행 추정치가 그리 멀지는 않지만 비용 추정치가 4 배 더 크게 보입니다. 이것은 아마도 문제가 아니지만 내가 할 수있는 일이 있습니까?

테이블이 항상 메모리에 없기 때문에 디스크 액세스가 문제가 될 수 있습니다.

EXPLAIN ANALYZE 
SELECT SUM("value") 
FROM "energy_energyentry" 
WHERE 
  "prop_id"=82411 
  AND "timestamp">'2014-06-11' 
  AND "timestamp"<'2014-11-11'
;
 Aggregate  (cost=214481.45..214481.46 rows=1 width=8) (actual time=51504.814..51504.814 rows=1 loops=1)
   ->  Index Scan using energy_energyentry_prop_id_timestamp_idx on  energy_energyentry (cost=0.00..214434.08 rows=18947 width=8) (actual time=136.030..51488.321 rows=13578 loops=1)
         Index Cond: ((prop_id = 82411) AND ("timestamp" > '2014-06-11 00:00:00+00'::timestamp with time zone) AND ("timestamp" < '2014-11-11 00:00:00+00'::timestamp with time zone))
 Total runtime: 51504.841 ms

더 빨리 만드는 방법에 대한 제안이 있으십니까?
나는 이상한 일을하지 않았다는 말만으로도 괜찮습니다.


1
테이블의 모양, 인덱스 및 데이터 확산을 알려주십시오.
Colin 't Hart

요청한 추가 정보를 추가했습니다. 내가 뭘 놓친 지 몰라
Exelian

2
이상 : Explain 분석이 표시 prop_time_idx하지만 테이블 정의가 표시 entry_prop_id_timestamp_idx됩니다. 이것이 동일한 인덱스입니까? 수정하십시오.
Colin 't Hart

비용 수치가 실제 시간 의 약 4 배 라는 사실에 대해 '원가 추정치가 4 배 더 큰 것으로 보인다'라고 언급 한 경우 ,이 둘은 서로 관련이 없습니다. 비용은 추정치 일 뿐이므로 쿼리 최적화 프로그램이 가장 적합한 계획을 선택할 수 있습니다. 이러한 맥락을 벗어나면 일반적으로 의미가 없습니다.
dezso

1
날짜 범위는 표의 몇 퍼센트를 나타 냅 prop니까 ( 의 값을 고려하지 않고 )? 적은 비율이면 인덱스 ("timestamp", prop)가 더 좋을 것입니다. 선행 열이 동일한 여러 인덱스 ( prop귀하의 경우)도 종종 중복됩니다.
Colin 't Hart

답변:


10

테이블이 크고 전체 테이블에 걸친 인덱스도 있습니다. 가정 :

  • 새 데이터 만 ( timestamp = now()) 입력
  • 기존 행은 변경되거나 삭제되지 않습니다.
  • 2012-01-01 이후 데이터가 있지만 쿼리는 주로 현재 연도 (?)

나는 부분적인 다중 열 (커버링!) 인덱스를 제안합니다 .

CREATE INDEX ON energy_energyentry (prop_id, "timestamp", value)
WHERE "timestamp" >= '2014-01-01 0:0';  -- adapt to your needs

정기적으로 쿼리되는 시간 범위 만 포함하십시오. 새로운 항목으로 시간이 지남에 따라 효율성이 저하됩니다. 때때로 색인을 다시 작성하십시오. (조회를 조정해야 할 수도 있습니다.) 아래 링크 된 답변을 참조하십시오.

마지막 열 값은 인덱스 전용 스캔 을 가져 오기 위해서만 포함됩니다 . @jjanes에서 이미 언급 한 것처럼 적극적인 자동 진공 설정은 가시성 맵을 최신 상태로 유지하는 데 도움이 될 수 있습니다 .

부분 인덱스는 RAM에보다 쉽게 ​​맞고 더 오래 있어야합니다.

WHERE플래너가 인덱스가 쿼리에 적용 가능한지 이해하도록하기 위해 쿼리 에이 조건 을 포함해야 할 수 있습니다 .

SELECT sum(value) AS sum_value
FROM   energy_energyentry
WHERE  prop_id = 82411 
AND   "timestamp" > '2014-06-11 0:0' 
AND   "timestamp" < '2014-11-11 0:0'
AND   "timestamp" >= '2014-01-01 0:0'; -- seems redundant, but may be needed

쿼리가 많은 행 ( rows=13578)을 합산하기 때문에 인덱스 전용 스캔으로도 시간이 걸릴 것입니다. 그래도 50 초 근처에 있으면 안됩니다. 중간 정도의 하드웨어에서 1 초도 채 걸리지 않습니다.

관련 (그러나 무시 CLUSTER하고 FILLFACTOR인덱스 만 스캔 할 수 있다면 무시 하고 둘 다 관련이 없습니다) :

따로 :에 대한 색인
현재 있으므로 (prop_id, "timestamp")추가 색인에 (prop_id)대한 가치가 다음보다 높을 수 있습니다.


Postgres는 BRIN 인덱스를 지원합니다. 여기서 유용할까요? postgres의 데이터에 약 1 억 4 천만 개의 행을 저장할 계획인데, BRIN이 큰 테이블에 사용하기에 적합한 인덱스입니까?
Arya

2

인덱스를 설정 (prop_id, "timestamp", "value")하면 인덱스 전용 스캔을 사용하여 테이블을 방문하지 않고도 값을 계산할 수 있습니다. 이것은 많은 무작위 디스크 액세스를 절약 할 수 있습니다.

최대한의 이익을 얻으려면 테이블 청소에 적극적이어야합니다. 인덱스 전용 스캔을 효율적으로 지원하려는 삽입 전용 테이블에 대해서는 기본 autovac 설정이 충분히 공격적이지 않습니다.


가치를 더하는 것은 실제로 흥미로울 수 있습니다. 내가 볼 수있는 진공 설정 또는 문서에 대한 제안 사항이 있습니까?
Exelian
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.