PostgreSQL에서 대량 업데이트 성능 최적화


37

Ubuntu 12.04에서 PG 9.1 사용

현재 데이터베이스에서 대량의 UPDATE 문 집합을 실행하는 데 최대 24 시간이 걸립니다.

UPDATE table
SET field1 = constant1, field2 = constant2, ...
WHERE id = constid

(우리는 ID로 식별 된 객체의 필드를 덮어 쓰고 있습니다.) 값은 외부 데이터 소스 (테이블의 DB에 아직없는)에서 가져옵니다.

테이블에는 각각 소수의 인덱스가 있으며 외래 키 제약 조건이 없습니다. 끝날 때까지 COMMIT이 수행되지 않습니다.

pg_dump전체 DB 를 가져 오는 데 2 ​​시간이 걸립니다 . 이것은 우리가 합리적으로 목표로 삼아야 할 기준선처럼 보입니다.

PostgreSQL의 데이터 세트를 다시 가져 오기 위해 재구성하는 사용자 정의 프로그램을 생성하는 데 부족한 경우, 대량의 UPDATE 성능을 가져 오기 성능에 더 가깝게 만들 수있는 방법이 있습니까? (이것은 로그 구조의 병합 트리가 잘 처리한다고 생각되는 영역이지만 PostgreSQL 내에서 할 수있는 일이 있는지 궁금합니다.)

몇 가지 아이디어 :

  • 모든 비 ID 지수를 버리고 나중에 재건하고 있습니까?
  • checkpoint_segments를 늘리지 만 실제로 장기적인 처리량을 유지하는 데 도움이됩니까?
  • 여기에 언급 된 기술을 사용 합니까? (새 데이터를 테이블로로드 한 다음 새 데이터에서 ID를 찾을 수없는 이전 데이터를 "병합")

기본적으로 시도해야 할 것이 많으며 가장 효과적인 것이 무엇인지 또는 다른 것을 간과하는지 확실하지 않습니다. 우리는 다음 며칠 동안 실험을 할 것이지만 여기서도 물을 것이라고 생각했습니다.

테이블에 동시로드가 있지만 읽기 전용입니다.


귀하의 질문에 중요한 정보가 없습니다 : 귀하의 Postgres 버전? 가치는 어디에서 오는가? 데이터베이스 외부의 파일처럼 들리지만 명확히하십시오. 목표 테이블에 동시로드가 있습니까? 그렇다면 정확히 무엇입니까? 아니면 떨어 뜨리고 다시 만들 여유가 있습니까? 외래 키는 없습니다.하지만 뷰와 같은 다른 종속 객체가 있습니까? 누락 된 정보로 질문을 편집하십시오. 댓글에 꽉 쥐지 마십시오.
Erwin Brandstetter

@ErwinBrandstetter 감사합니다, 내 질문을 업데이트했습니다.
Yang

explain analyze조회를 위해 색인을 사용하고 있음을 통해 확인 했다고 가정 합니까?
rogerdpack

답변:


45

가정

Q에 정보가 없기 때문에 다음과 같이 가정합니다.

  • 데이터는 데이터베이스 서버의 파일에서 가져옵니다.
  • 데이터는 목표 테이블과 일치하도록 행당 고유 한COPY 출력 과 같이 형식이 지정 됩니다. 그렇지 않은 경우 먼저 형식을 올바르게 지정하거나 옵션을 사용 하여 형식을 처리하십시오. id
    COPY
  • 목표 테이블 또는 대부분의 행에서 모든 단일 행을 갱신하고 있습니다.
  • 대상 테이블을 삭제하고 다시 만들 수 있습니다.
    이는 동시 액세스가 없음을 의미 합니다. 그렇지 않으면이 관련 답변을 고려하십시오.
  • 인덱스를 제외하고는 종속 개체가 전혀 없습니다.

해결책

세 번째 글 머리 기호링크에 설명 된 것과 비슷한 방법으로 접근하는 것이 좋습니다 . 주요 최적화

임시 테이블을 만들려면 더 간단하고 빠른 방법이 있습니다.

CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;

데이터베이스 내부UPDATE임시 테이블에서 하나의 빅 데이터가 데이터베이스 외부에서 개별 업데이트보다 몇 배 빠릅니다.

에서 의 PostgreSQL의 MVCC 모델 , UPDATE수단은 새 행 버전을 생성하고 삭제 된 것으로 이전을 표시합니다. 그 비용 INSERTDELETE합산 된 것만큼이나 비쌉니다 . 또한 죽은 튜플이 많이 남습니다. 어쨌든 전체 테이블을 업데이트하기 때문에 새 테이블을 만들고 이전 테이블을 삭제하는 것이 전체적으로 더 빠릅니다.

경우 당신이 충분한 RAM, 세트가 temp_buffersRAM에 임시 테이블을 보유 할만큼 충분히 높은 (전용이 세션에!) - 다른 작업을 수행하기 전에.

필요한 RAM의 양을 추정하려면 작은 샘플로 테스트를 실행하고 db object size 함수를 사용 하십시오 .

SELECT pg_size_pretty(pg_relation_size('tmp_tbl'));  -- complete size of table
SELECT pg_column_size(t) FROM tmp_tbl t LIMIT 10;  -- size of sample rows

완전한 스크립트

SET temp_buffers = '1GB';        -- example value

CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;

COPY tmp_tbl FROM '/absolute/path/to/file';

CREATE TABLE tbl_new AS
SELECT t.col1, t.col2, u.field1, u.field2
FROM   tbl     t
JOIN   tmp_tbl u USING (id);

-- Create indexes like in original table
ALTER TABLE tbl_new ADD PRIMARY KEY ...;
CREATE INDEX ... ON tbl_new (...);
CREATE INDEX ... ON tbl_new (...);

-- exclusive lock on tbl for a very brief time window!
DROP TABLE tbl;
ALTER TABLE tbl_new RENAME TO tbl;

DROP TABLE tmp_tbl; -- will also be dropped at end of session automatically

동시 부하

테이블의 동시 작업 (시작시 가정에서 배제한)은 테이블이 끝 근처에서 잠기고 트랜잭션이 커밋 되 자마자 실패하면 테이블 이름이 즉시 OID로 확인되기 때문에 대기하지만 새 테이블의 OID가 다릅니다. 테이블은 일관성을 유지하지만 동시 작업에는 예외가 발생하여 반복해야합니다. 이 관련 답변의 세부 사항 :

업데이트 경로

UPDATE경로를 이동 해야하는 경우 업데이트 중에 필요하지 않은 인덱스를 삭제하고 나중에 다시 만드십시오. 모든 개별 행에 대해 인덱스를 업데이트하는 것보다 한 조각으로 인덱스를 만드는 것이 훨씬 저렴합니다. 이것은 또한 HOT 업데이 트를 허용 할 수 있습니다 .

내가 사용하여 유사한 절차를 설명 UPDATESO에이 밀접하게 관련 대답 .

 


1
실제로 대상 테이블에서 행의 20 %를 업데이트하고 있습니다. 모두가 아니라 임의 업데이트보다 병합이 더 좋을 정도로 큰 부분입니다.
Yang

1
@AryehLeibTaurog은 :부터 그 일이되어서는 안 DROP TABLE을한다 Access Exclusive Lock. 어느 쪽이든, 나는 이미 내 대답의 맨 위에 전제 조건을 나열했습니다 You can afford to drop and recreate the target table.. 트랜잭션 시작시 테이블을 잠그는 것이 도움이 될 수 있습니다. 상황에 대한 모든 관련 세부 정보를 사용하여 새로운 질문 을 시작하는 것이 좋습니다 .
Erwin Brandstetter

1
@ErwinBrandstetter 흥미로운. 서버 버전에 의존하는 것 같습니다. psycopg2 어댑터psql 클라이언트를 사용 하여 8.4 및 9.1의 오류를 재현했습니다 . 9.3에서는 오류가 없습니다. 첫 번째 스크립트에서 내 의견을 참조하십시오. 여기에 게시 할 질문이 있는지 확실하지 않지만 postgresql 목록 중 하나에 대한 정보를 요청하는 것이 좋습니다.
Aryeh Leib Taurog

1
프로세스를 자동화하기 위해 파이썬으로 간단한 도우미 클래스 를 작성했습니다 .
Aryeh Leib Taurog

3
매우 유용한 답변입니다. 약간의 변화로, 하나는 원래 테이블에서 업데이트 할 경우에만 업데이트 할 열 참조의 열 삭제 열 임시 테이블을 만들 수 있습니다, 다음 테이블을 사용하여 병합 CREATE TABLE tbl_new AS SELECT t.*, u.field1, u.field2 from tbl t NATURAL LEFT JOIN tmp_tbl u;, LEFT JOIN어떤 업데이트가되지 않은 행을 유지 할 수 있도록. 물론은 NATURAL유효한 USING()또는 로 변경할 수 있습니다 ON.
Skippy le Grand Gourou

2

구조화 된 파일에서 데이터를 사용할 수있는 경우 외부 데이터 랩퍼로 데이터를 읽고 대상 테이블에서 병합을 수행 할 수 있습니다.


3
"대상 테이블에서 병합"이란 무엇입니까? FDW를 임시 테이블에 복사하는 것보다 FDW를 사용하는 것이 더 좋은 이유는 무엇입니까?
Yang

MERGE sql 문에서와 같이 "병합" FDW를 사용하면 데이터를 임시 테이블에 복사하는 추가 단계없이이를 수행 할 수 있습니다. 전체 데이터 세트를 바꾸지 않고 파일에 현재 데이터 세트의 변경 사항을 나타내지 않는 일정량의 데이터가 있다고 가정합니다. 대량이 변경된 경우 전체 테이블을 교체하는 것이 좋습니다.
David Aldridge

1
@DavidAldridge : SQL : 2003 표준에 정의되어 있지만 MERGEPostgreSQL (아직) 에는 구현되어 있지 않습니다 . 다른 RDBMS에서의 구현은 상당히 다릅니다. MERGE및 의 태그 정보를 고려하십시오 UPSERT.
Erwin Brandstetter

@ErwinBrandstetter [글 러스] 아 그래요. 잘 병합은 내가 생각하는 케이크에 착빙입니다. 가져 오기-임시 테이블 단계없이 데이터에 액세스하는 것은 실제로 FDW 기술의 핵심입니다.
David Aldridge
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.