읽기 성능을 위해 PostgreSQL 구성


39

우리 시스템은 많은 양의 데이터를 작성합니다. 쓰기 성능은 우리의 요구에 충분하지만 읽기 성능이 너무 느립니다.

기본 키 (제약) 구조는 모든 테이블에서 유사합니다.

timestamp(Timestamp) ; index(smallint) ; key(integer).

테이블은 수백만 행, 심지어 수십억 행을 가질 수 있으며 읽기 요청은 일반적으로 특정 기간 (타임 스탬프 / 인덱스) 및 태그에 대한 것입니다. 약 200k 줄을 반환하는 쿼리가 일반적입니다. 현재 초당 약 15k 라인을 읽을 수 있지만 10 배 더 빨라야합니다. 이것이 가능합니까? 그렇다면 어떻게됩니까?

참고 : PostgreSQL은 소프트웨어와 함께 제공되므로 하드웨어는 클라이언트마다 다릅니다.

테스트에 사용되는 VM입니다. VM의 호스트는 24.0GB의 RAM이있는 Windows Server 2008 R2 x64입니다.

서버 사양 (가상 머신 VMWare)

Server 2008 R2 x64
2.00 GB of memory
Intel Xeon W3520 @ 2.67GHz (2 cores)

postgresql.conf 최적화

shared_buffers = 512MB (default: 32MB)
effective_cache_size = 1024MB (default: 128MB)
checkpoint_segment = 32 (default: 3)
checkpoint_completion_target = 0.9 (default: 0.5)
default_statistics_target = 1000 (default: 100)
work_mem = 100MB (default: 1MB)
maintainance_work_mem = 256MB (default: 16MB)

테이블 정의

CREATE TABLE "AnalogTransition"
(
  "KeyTag" integer NOT NULL,
  "Timestamp" timestamp with time zone NOT NULL,
  "TimestampQuality" smallint,
  "TimestampIndex" smallint NOT NULL,
  "Value" numeric,
  "Quality" boolean,
  "QualityFlags" smallint,
  "UpdateTimestamp" timestamp without time zone, -- (UTC)
  CONSTRAINT "PK_AnalogTransition" PRIMARY KEY ("Timestamp" , "TimestampIndex" , "KeyTag" ),
  CONSTRAINT "FK_AnalogTransition_Tag" FOREIGN KEY ("KeyTag")
      REFERENCES "Tag" ("Key") MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
  OIDS=FALSE,
  autovacuum_enabled=true
);

질문

pgAdmin3에서 쿼리를 실행하는 데 약 30 초가 걸리지 만 가능한 경우 5 초 이내에 동일한 결과를 원합니다.

SELECT 
    "AnalogTransition"."KeyTag", 
    "AnalogTransition"."Timestamp" AT TIME ZONE 'UTC', 
    "AnalogTransition"."TimestampQuality", 
    "AnalogTransition"."TimestampIndex", 
    "AnalogTransition"."Value", 
    "AnalogTransition"."Quality", 
    "AnalogTransition"."QualityFlags", 
    "AnalogTransition"."UpdateTimestamp"
FROM "AnalogTransition"
WHERE "AnalogTransition"."Timestamp" >= '2013-05-16 00:00:00.000' AND "AnalogTransition"."Timestamp" <= '2013-05-17 00:00:00.00' AND ("AnalogTransition"."KeyTag" = 56 OR "AnalogTransition"."KeyTag" = 57 OR "AnalogTransition"."KeyTag" = 58 OR "AnalogTransition"."KeyTag" = 59 OR "AnalogTransition"."KeyTag" = 60)
ORDER BY "AnalogTransition"."Timestamp" DESC, "AnalogTransition"."TimestampIndex" DESC
LIMIT 500000;

설명 1

"Limit  (cost=0.00..125668.31 rows=500000 width=33) (actual time=2.193..3241.319 rows=500000 loops=1)"
"  Buffers: shared hit=190147"
"  ->  Index Scan Backward using "PK_AnalogTransition" on "AnalogTransition"  (cost=0.00..389244.53 rows=1548698 width=33) (actual time=2.187..1893.283 rows=500000 loops=1)"
"        Index Cond: (("Timestamp" >= '2013-05-16 01:00:00-04'::timestamp with time zone) AND ("Timestamp" <= '2013-05-16 15:00:00-04'::timestamp with time zone))"
"        Filter: (("KeyTag" = 56) OR ("KeyTag" = 57) OR ("KeyTag" = 58) OR ("KeyTag" = 59) OR ("KeyTag" = 60))"
"        Buffers: shared hit=190147"
"Total runtime: 3863.028 ms"

설명 2

최신 테스트에서 데이터를 선택하는 데 7 분이 걸렸습니다! 아래를보십시오 :

"Limit  (cost=0.00..313554.08 rows=250001 width=35) (actual time=0.040..410721.033 rows=250001 loops=1)"
"  ->  Index Scan using "PK_AnalogTransition" on "AnalogTransition"  (cost=0.00..971400.46 rows=774511 width=35) (actual time=0.037..410088.960 rows=250001 loops=1)"
"        Index Cond: (("Timestamp" >= '2013-05-22 20:00:00-04'::timestamp with time zone) AND ("Timestamp" <= '2013-05-24 20:00:00-04'::timestamp with time zone) AND ("KeyTag" = 16))"
"Total runtime: 411044.175 ms"

답변:


52

데이터 정렬 및 저장 크기

실제로 튜플 당 오버 헤드는 튜플 헤더의 경우 24 바이트에 항목 포인터의 경우 4 바이트입니다.
이 관련 답변의 계산에 대한 자세한 내용 :

SO에 대한이 관련 답변의 데이터 정렬 및 패딩 기본 사항 :

기본 키에 대한 세 개의 열이 있습니다.

PRIMARY KEY ("Timestamp" , "TimestampIndex" , "KeyTag")

"Timestamp"      timestamp (8 bytes)
"TimestampIndex" smallint  (2 bytes)
"KeyTag"         integer   (4 bytes)

결과 :

 페이지 헤더의 4 바이트 항목 포인터 (8 바이트의 배수로 계산되지 않음)
---
튜플 헤더의 경우 23 바이트
 데이터 정렬을위한 1 바이트 패딩 (또는 NULL 비트 맵)
 8 바이트 "타임 스탬프"
 2 바이트 "TimestampIndex"
 데이터 정렬을위한 2 바이트 패딩
 4 바이트 "KeyTag" 
 가장 가까운 8 바이트의 배수로 채워지는 0
-----
튜플 당 44 바이트

이 관련 답변에서 객체 크기 측정에 대한 자세한 내용 :

여러 열 인덱스의 열 순서

다음 두 가지 질문과 답변을 읽고 이해하십시오.

인덱스 (기본 키)를 사용하는 방법에 따라 정렬 단계없이 행을 검색 할 수 있습니다 LIMIT. 특히 매력적입니다 . 그러나 행을 검색 하는 것은 매우 비싸 보입니다.

일반적으로 다중 열 인덱스에서 "같음"열이 먼저 가고 "범위"열이 마지막에 있어야합니다.

따라서, 반전과 추가 인덱스 시도 열 순서를 :

CREATE INDEX analogransition_mult_idx1
   ON "AnalogTransition" ("KeyTag", "TimestampIndex", "Timestamp");

데이터 배포에 따라 다릅니다. 그러나 millions of row, even billion of rows이것이 훨씬 빠를 수 있습니다.

튜플 크기는 데이터 정렬 및 패딩으로 인해 8 바이트 더 큽니다. 이것을 일반 인덱스로 사용하는 경우 세 번째 열을 삭제하려고 할 수 있습니다 "Timestamp". 정렬에 도움이 될 수 있으므로 약간 빠를 수도 있고 아닐 수도 있습니다.

두 인덱스를 모두 유지하려고 할 수 있습니다. 여러 가지 요인에 따라 특히 작은 색인을 사용하면 원본 색인이 선호 될 수 있습니다 LIMIT.

autovacuum 및 테이블 통계

테이블 통계가 최신 상태 여야합니다. 나는 당신이 autovacuum을 실행하고 있다고 확신합니다 .

테이블이 거대하고 올바른 쿼리 계획에 중요한 통계이기 때문에 관련 열에 대한 통계 목표 를 크게 늘릴 것입니다 .

ALTER TABLE "AnalogTransition" ALTER "Timestamp" SET STATISTICS 1000;

... 수십억 개의 행으로 더 높아집니다. 최대 값은 10000이고 기본값은 100입니다.

WHERE또는 ORDER BY절에 관련된 모든 열에 대해 그렇게하십시오 . 그런 다음을 실행하십시오 ANALYZE.

테이블 레이아웃

그 동안 데이터 정렬 및 패딩에 대해 배운 내용을 적용하면이 최적화 된 테이블 레이아웃은 디스크 공간을 절약하고 성능을 약간 향상시킵니다 (pk & fk 무시).

CREATE TABLE "AnalogTransition"(
  "Timestamp" timestamp with time zone NOT NULL,
  "KeyTag" integer NOT NULL,
  "TimestampIndex" smallint NOT NULL,
  "TimestampQuality" smallint,
  "UpdateTimestamp" timestamp without time zone, -- (UTC)
  "QualityFlags" smallint,
  "Quality" boolean,
  "Value" numeric
);

CLUSTER / pg_repack

특정 인덱스를 사용하는 쿼리 (원래 인덱스 또는 제안 된 대체 인덱스)에 대한 읽기 성능을 최적화하기 위해 인덱스의 실제 순서대로 테이블을 다시 작성할 수 있습니다. CLUSTER그렇게하지만 다소 침습적이며 작업 기간 동안 독점 잠금이 필요합니다. pg_repack테이블에 대한 독점 잠금 없이도 동일한 작업을 수행 할 수있는보다 정교한 대안입니다.
훨씬 적은 수의 테이블 블록을 읽어야하므로 이는 큰 테이블에 도움이 될 수 있습니다.

일반적으로 2GB의 실제 RAM으로는 수십억 개의 행을 빠르게 처리하기에 충분하지 않습니다. 더 많은 RAM이 먼 길을 갈 수 있습니다. 적응 형 설정이 수반됩니다 effective_cache_size.


2
KeyTag에만 간단한 색인을 추가했는데 지금은 매우 빠른 것 같습니다. 또한 데이터 정렬에 대한 권장 사항을 적용 할 것입니다. 고마워요!
JPelletier

9

따라서 계획에서 한 가지를 볼 수 있습니다. 인덱스가 부풀어 오거나 (기본 테이블과 함께) 단순히 이런 종류의 쿼리에 좋지 않습니다 (위의 최신 의견 에서이 문제를 해결하려고했습니다).

색인의 한 행에는 14 바이트의 데이터 (및 일부는 헤더)가 있습니다. 이제 계획에 주어진 숫자를 계산하면 190147 페이지에서 500,000 개의 행을 얻을 수 있습니다. 즉, 평균 페이지 당 3 행 미만의 유용한 행, 즉 8kb 페이지 당 약 37 바이트입니다. 이것은 매우 나쁜 비율입니까? 인덱스의 첫 번째 열이 Timestamp필드이고 쿼리에서 범위로 사용되므로 플래너는 인덱스를 선택하여 일치하는 행을 찾을 수 있습니다. 그러나 조건에 대해서는 TimestampIndex언급되어 있지 않으므로 해당 값이 인덱스 페이지에 임의로 표시 WHERE되므로 필터링을 사용하는 KeyTag것이 효과적이지 않습니다.

따라서 한 가지 가능성은 인덱스 정의를

CONSTRAINT "PK_AnalogTransition" PRIMARY KEY ("Timestamp", "KeyTag", "TimestampIndex")

또는 시스템로드가 주어진 경우이 색인을 새 색인으로 작성하십시오.

CREATE INDEX CONCURRENTLY "idx_AnalogTransition" 
    ON "AnalogTransition" ("Timestamp", "KeyTag", "TimestampIndex");
  • 이 작업은 시간이 다소 걸리지 만 그 동안 계속 작업 할 수 있습니다.)

또 다른 가능성은 인덱스 페이지의 많은 부분이 데드 행에 의해 점유 될 수 있으며,이 행은 진공 청소기로 제거 할 수 있습니다. 설정으로 테이블을 autovacuum_enabled=true만들었지 만 자동 진공 청소기를 시작한 적이 있습니까? 아니면 VACUUM수동으로 실행 하시겠습니까?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.