Postgres UPDATE가 왜 39 시간이 걸립니까?


17

~ 210 만 행의 Postgres 테이블이 있습니다. 아래 업데이트를 실행했습니다.

WITH stops AS (
    SELECT id,
           rank() OVER (ORDER BY offense_timestamp,
                     defendant_dl,
                     offense_street_number,
                     offense_street_name) AS stop
    FROM   consistent.master
    WHERE  citing_jurisdiction=1
)

UPDATE consistent.master
SET arrest_id=stops.stop
FROM stops
WHERE master.id = stops.id;

이 쿼리를 실행하는 데 39 시간이 걸렸습니다. 나는 이것을 4 (물리적) 코어 i7 Q720 랩탑 프로세서, 많은 RAM, 대부분의 시간 동안 달리는 아무것도 실행하지 않습니다. HDD 공간 제약이 없습니다. 최근에 테이블을 진공 청소기, 분석 및 재 색인했습니다.

쿼리가 실행되는 전체 시간, 적어도 초기 WITH완료 후 CPU 사용량은 일반적으로 낮았으며 HDD는 100 % 사용 중입니다. HDD가 너무 열심히 사용되어 다른 앱이 평소보다 훨씬 느리게 실행되었습니다.

랩톱의 전원 설정이 고성능 (Windows 7 x64)으로 설정되었습니다 .

설명은 다음과 같습니다.

Update on master  (cost=822243.22..1021456.89 rows=2060910 width=312)
  CTE stops
    ->  WindowAgg  (cost=529826.95..581349.70 rows=2060910 width=33)
          ->  Sort  (cost=529826.95..534979.23 rows=2060910 width=33)
                Sort Key: consistent.master.offense_timestamp, consistent.master.defendant_dl, consistent.master.offense_street_number, consistent.master.offense_street_name
                ->  Seq Scan on master  (cost=0.00..144630.06 rows=2060910 width=33)
                      Filter: (citing_jurisdiction = 1)
  ->  Hash Join  (cost=240893.51..440107.19 rows=2060910 width=312)
        Hash Cond: (stops.id = consistent.master.id)
        ->  CTE Scan on stops  (cost=0.00..41218.20 rows=2060910 width=48)
        ->  Hash  (cost=139413.45..139413.45 rows=2086645 width=268)
              ->  Seq Scan on master  (cost=0.00..139413.45 rows=2086645 width=268)

citing_jurisdiction=1수만 행만 제외합니다. 이 WHERE조항을 사용 하더라도 여전히 2 백만 행 이상을 운영하고 있습니다.

하드 드라이브는 TrueCrypt 7.1a로 전체 드라이브 암호화됩니다. 충분히 그 감속의 조금 아래 것들,하지만 쿼리가 수행하게하기 위해 많은 시간을.

WITH부품은 실행하는 데 약 3 분이 걸립니다.

arrest_id필드는 외부 키 인덱스 없었다. 이 테이블에는 8 개의 인덱스와 2 개의 외래 키가 있습니다. 쿼리의 다른 모든 필드는 색인화됩니다.

arrest_id필드에는를 제외한 제약 조건이 없습니다 NOT NULL.

테이블에는 총 32 개의 열이 있습니다.

arrest_id문자 가변 (20) 유형 입니다. rank()숫자 값을 생성 한다는 것을 알고 있지만 이 필드에 숫자가 아닌 데이터를 사용하는 다른 행이 있기 때문에 문자 가변 (20)citing_jurisdiction<>1 을 사용해야합니다.

이 ( arrest_id가)있는 모든 행 의 입력란이 비어 citing_jurisdiction=1있습니다.

개인용 고급형 노트북입니다 (1 년 전). 나는 유일한 사용자입니다. 다른 쿼리 나 작업이 실행되지 않았습니다. 잠그는 것 같지 않습니다.

이 테이블 또는 데이터베이스의 어느 위치에도 트리거가 없습니다.

이 데이터베이스의 다른 작업에는 시간이 걸리지 않습니다. 적절한 인덱싱을 사용하면 SELECT일반적으로 쿼리가 매우 빠릅니다.


사람들은 Seq Scan조금 무서운 ...입니다
rogerdpack

답변:


18

최근 350 만 행의 테이블에서 비슷한 일이 발생했습니다. 업데이트가 완료되지 않았습니다. 많은 실험과 좌절 끝에 마침내 범인을 발견했습니다. 업데이트되는 테이블의 인덱스로 밝혀졌습니다.

해결책은 update 문을 실행하기 전에 업데이트중인 테이블의 모든 인덱스를 삭제하는 것입니다. 일단 그렇게하면 몇 분 안에 업데이트가 완료되었습니다. 업데이트가 완료되면 인덱스를 다시 작성하여 업무를 다시 시작했습니다. 이것은 현재로서는 도움이되지 않지만 다른 사람이 답을 찾고있을 수 있습니다.

데이터를 가져 오는 테이블의 인덱스를 유지합니다. 인덱스를 계속 업데이트 할 필요가 없으며 업데이트 할 데이터를 찾는 데 도움이됩니다. 느린 랩톱에서 잘 작동했습니다.


3
나는 당신에게 최고의 답변을 전환하고 있습니다. 이 게시물을 게시 한 후 업데이트중인 열에 이미 값이 있고 색인이없는 경우에도 색인에 문제가있는 다른 상황이 발생했습니다. Postgres는 다른 열의 인덱스를 관리하는 방법에 문제가있는 것 같습니다. 테이블을 변경하는 유일한 방법은 인덱싱되지 않은 열을 업데이트하고 해당 열의 행에 할당 된 공간을 늘리지 않는 경우 이러한 다른 인덱스가 업데이트 쿼리 시간을 연장 할 이유가 없습니다.
Aren Cambre

1
감사! 그것이 다른 사람들을 돕기를 바랍니다. 그것은 겉보기에는 매우 간단한 것으로 인해 몇 시간의 두통을 덜어주었습니다.
JC Avena

5
@ArenCambre-이유가 있습니다. PostgreSQL은 전체 행을 다른 위치에 복사하고 이전 버전을 삭제 된 것으로 표시합니다. PostgreSQL이 MVCC (Multi-Version Concurrency Control)를 구현하는 방법입니다.
Piotr Findeisen 2016 년

내 질문은 ... 왜 범인인가? 참조 stackoverflow.com/a/35660593/32453
rogerdpack

15

가장 큰 문제는 랩톱 하드 드라이브에서 많은 양의 쓰기, 찾기, 찾기 작업을 수행하는 것입니다. 당신이 무엇을하든, 특히 많은 노트북에 탑재 된 5400RPM 드라이브가 느리다면 결코 빠르지 않을 것입니다.

TrueCrypt는 쓰기 작업을 "약간"보다 느리게합니다. 읽기는 상당히 빠르지 만 쓰기는 RAID 5를 빠르게 보이게합니다. TrueCrypt 볼륨에서 DB를 실행하면 쓰기, 특히 임의 쓰기에 대한 고문이 발생합니다.

이 경우 쿼리 최적화를 위해 시간을 낭비하고 있다고 생각합니다. 어쨌든 대부분의 행을 다시 작성하고 있으며 끔찍한 쓰기 상황에서는 느려질 것 입니다. 내가 추천하는 것은 :

BEGIN;
SELECT ... INTO TEMPORARY TABLE master_tmp ;
TRUNCATE TABLE consistent.master;
-- Now DROP all constraints on consistent.master, then:
INSERT INTO consistent.master SELECT * FROM master_tmp;
-- ... and re-create any constraints.

UPDATE에는 스토리지를 죽일 수 있는 상당히 임의의 쓰기 패턴이 있기 때문에 제약 조건 만 삭제하고 다시 만드는 것보다 빠르다고 생각합니다 . 제약이없는 두 개의 대량 삽입 (하나는 로그되지 않은 테이블에, 하나는 WAL 로그 된 테이블에)이 더 빠를 것입니다.

절대적으로 최신 백업이 있고 백업에서 데이터베이스를 복원하지 않아도 되는 경우 PostgreSQL을 fsync=off매개 변수와 함께이 대량 작업에 대해 full_page_writes=off 일시적으로 다시 시작할 수도 있습니다. 정전 또는 OS 충돌과 같은 예기치 않은 문제는 데이터베이스를 복구 할 수없는 상태로 둡니다 fsync=off.

"로깅 없음"에 해당하는 POSTGreSQL은 로깅되지 않은 테이블을 사용하는 것입니다. 이 로그되지 않은 테이블은 더러워진 상태에서 DB가 비정상적으로 종료되면 잘립니다. 그들이 할 수 있도록 적어도, 당신의 쓰기로드를 반감 그리고이 추구 수를 줄일 수 로깅되지 않은 테이블을 사용하여 LOT 빨리.

Oracle과 마찬가지로 대규모 일괄 업데이트 후에는 인덱스를 삭제 한 다음 다시 생성하는 것이 좋습니다. PostgreSQL의 플래너는 큰 업데이트가 발생하고 인덱스 업데이트를 일시 중지 한 다음 마지막에 인덱스를 다시 빌드 할 수 없습니다. 그것이 가능하더라도, 특히 사전에 가치가있는 시점을 파악하기는 매우 어려울 것입니다.


이 답변은 많은 양의 쓰기와 끔찍한 암호화 성능 및 느린 랩톱 드라이브에 있습니다. 또한 8 인덱스의 존재는 많은 여분의 쓰기와 패배에의 적용 생산하고 있습니다 것 HOT 그래서 인덱스를 삭제하고 낮은 사용에 블록 행 업데이트를 FILLFACTOR 행 마이그레이션의 톤 방지 할 수있는 테이블을
dbenhur

1
채우기 요인으로 HOT 기회를 높이는 데 호의적 인 전화-TrueCrypt를 사용하여 거대한 블록에서 블록 읽기-다시 쓰기주기를 강요하더라도 많은 도움이 될지 확신하지 못합니다. 테이블을 늘리는 것은 최소한 선형적인 쓰기 블록을 수행하기 때문에 행 마이그레이션이 더 빠를 수도 있습니다 .
Craig Ringer 2014 년

2.5 년 후 나는 비슷한 일을하지만 더 큰 테이블에서 일하고 있습니다. 확인하기 위해 업데이트하는 단일 열이 색인화되지 않은 경우에도 모든 색인을 삭제하는 것이 좋습니다.
Aren Cambre

1
@ArenCambre이 경우 ... 글쎄, 복잡합니다. 대부분의 업데이트를 사용할 수있는 HOT경우 인덱스를 그대로 두는 것이 좋습니다. 그렇지 않다면 삭제하고 다시 만들고 싶을 것입니다. 열은 인덱싱되지 않지만 HOT 업데이트를 수행하려면 동일한 페이지에 여유 공간이 있어야하므로 테이블에 사용 가능한 공간이 얼마나 있는지에 따라 다릅니다. 가장 많이 쓰는 경우 모든 인덱스를 삭제한다고 말하고 싶습니다. 로트가 많이 업데이트되면 구멍이 생겼을 수도 있습니다. 도구를 좋아 pageinspect하고 pg_freespacemap깡통 도움이 결정됩니다.
Craig Ringer

감사. 이 경우 모든 행에 이미 항목이있는 부울 열입니다. 일부 행의 항목을 변경하고있었습니다. 방금 확인했습니다. 모든 인덱스를 삭제 한 후 업데이트는 2 시간 밖에 걸리지 않았습니다. 미리 업데이트가 너무 오래 걸리기 때문에 18 시간 후에 업데이트를 중지해야했습니다. 이것은 업데이트되고있는 열이 확실히 색인화되지 않았다는 사실에도 불구하고 있습니다.
Aren Cambre

2

누군가 Postgres에 대한 더 나은 답변을 제공 할 것입니다. 그러나 여기에는 Oracle 관점에서 적용 할 수있는 몇 가지 관찰 결과가 있습니다 (주석이 의견 필드에 비해 너무 깁니다).

내 첫 번째 관심사는 한 트랜잭션에서 2 백만 행을 업데이트하려고합니다. Oracle에서는 업데이트 된 각 블록의 이전 이미지를 작성하여 다른 세션이 수정 된 블록을 읽지 않고도 일관된 읽기를 유지하고 롤백 할 수 있도록합니다. 그것은 긴 롤백입니다. 일반적으로 작은 청크로 거래를하는 것이 좋습니다. 한 번에 1,000 개의 레코드를 말합니다.

테이블에 인덱스가 있고 유지 관리 중에 테이블이 작동하지 않는 것으로 간주되는 경우 큰 작업 전에 인덱스를 제거한 다음 나중에 다시 작성하는 것이 좋습니다. 그러면 업데이트 된 각 레코드와 함께 인덱스를 지속적으로 유지하려고 노력하는 것이 더 저렴합니다.

Oracle은 저널링을 중지하기 위해 명령문에 "로깅 없음"힌트를 허용합니다. 그것은 문장의 속도를 높이지만 DB를 "복구 할 수없는"상황으로 남겨 둡니다. 따라서 이전에 백업 한 후 즉시 다시 백업 할 수 있습니다. Postgres에 비슷한 옵션이 있는지 모르겠습니다.


PostgreSQL은 긴 롤백에 문제가 없으며 존재하지 않습니다. ROLBACK은 트랜잭션 규모에 관계없이 PostgreSQL에서 매우 빠릅니다. Oracle! = PostgreSQL
Frank Heikens

@FrankHeikens 감사합니다. 흥미 롭습니다. Postgres에서 저널링이 작동하는 방식을 읽어야합니다. 트랜잭션의 전체 개념을 작동 시키려면 트랜잭션 중에 두 가지 버전의 데이터, 즉 이전 이미지와 이후 이미지를 유지해야합니다. 이것이 제가 언급 한 메커니즘입니다. 어쨌든 거래를 유지하는 데 필요한 리소스가 너무 비쌀 수있는 임계 값이 있다고 생각합니다.
Glenn

2
@Glenn postgres는 테이블 자체에 행 버전을 유지합니다 . 자세한 내용은 여기 를 참조 하십시오 . 타협은 '죽은'튜플이 매달려 있다는 것인데, postgres의 '진공'과 비동기 적으로 정리됩니다 (오라클은 테이블 자체에 '죽은'행이 없기 때문에 진공이 필요 없습니다)
Jack은 말합니다. try atanswers.xyz

오히려 뒤늦게 당신이있는 거 환영하고 : 사이트 :-)에 오신 것을 환영합니다
잭 topanswers.xyz 시도라고

@Glenn PostgreSQL의 행 버전 동시성 제어에 대한 표준 문서는 postgresql.org/docs/current/static/mvcc-intro.html 이며 읽을 가치가 있습니다. wiki.postgresql.org/wiki/MVCC 도 참조하십시오 . 데드 행을 가진 MVCC VACUUM는 응답의 절반에 불과합니다. PostgreSQL은 또한 소위 "미리 쓰기 로그"(효과적으로 저널)를 사용하여 원자 적 커밋을 제공하고 부분적인 쓰기 등으로부터 보호합니다. postgresql.org/docs/current/static/wal-intro.html
Craig Ringer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.