PostgreSQL에서 매우 느린 삭제, 해결 방법?


30

PostgreSQL 9.2에 약 70 개의 테이블이있는 기본 스키마와 각각 30 개의 테이블로 구성된 동일한 구조의 클라이언트 당 스키마가있는 데이터베이스가 있습니다. 클라이언트 스키마에는 기본 스키마를 참조하는 외래 키가 있으며 다른 방법은 아닙니다.

방금 이전 버전에서 가져온 실제 데이터로 데이터베이스를 채우기 시작했습니다. 기본 스키마의 매우 중앙 테이블에서 대량 삭제를 수행해야 할 때 DB는 약 1.5GB에 도달했습니다 (주 내에 몇 10GB로 증가 할 것으로 예상 됨). 관련된 모든 외래 키는 ON DELETE CASCADE로 표시됩니다.

시간이 오래 걸린다는 것은 놀라운 일이 아니었지만 12 시간 후에는 DB를 삭제하고 마이그레이션을 다시 시작하는 것이 더 나아 졌다는 것이 분명해졌습니다. 그러나 나중에 DB가 작동하고 훨씬 더 큰 상태에서이 작업을 반복해야한다면 어떻게해야합니까? 더 빠른 대안이 있습니까?

중앙 테이블에서 가장 먼 테이블에서 시작하여 테이블별로 종속 행을 삭제하는 종속 테이블을 탐색하는 스크립트를 작성하면 훨씬 더 빠릅니까?

중요한 것은 일부 테이블에 트리거가 있다는 것입니다.


4
5 년 후, 나는 수용된 답변을 바꾸고있다. 느린 DELETE는 거의 항상 삭제되는 테이블을 직접 또는 간접적으로 참조하는 외래 키의 인덱스가 누락되어 발생합니다. 솔루션은 거의 항상 (예 : 누락 된 인덱스 추가) 실행 속도를 높이고 모든 트리거를 비활성화하지는 않지만 DELETE 문에서 실행되는 트리거로 인해 속도가 느려질 수도 있습니다.
jd.

답변:


29

나는 비슷한 문제가 있었다. 결과적으로 ON DELETE CASCADE계단식 삭제는 엄청나게 느려서 트리거가 상당히 느려졌습니다.

참조 테이블의 외래 키 필드에 인덱스를 생성하여 문제를 해결했으며 삭제에 몇 시간이 걸리는 데 몇 초가 걸렸습니다.


와우, 이것은 몇 분 안에 8M 레코드를 삭제하는 데 도움이되었습니다. 그러나 내가 이해하지 못하는 것은 내 테이블이 다른 테이블에 대한 참조 만 보유하고 다른 테이블은 내 테이블에 대한 참조를 보유하지 않는다는 것입니다. 그렇다면 여기서 효과는 정확히 무엇입니까? (사용하지 않음 ON DELETE CASCADE)
msrd0

2
이것은 나를 위해 그것을 해결했다. 이 작업을 시도하는 사람 EXPLAIN (ANALYZE, BUFFERS)은 단일 행 삭제에 대한 쿼리를 수행 할 수 있으며 어떤 외래 키 제약 조건이 가장 오래 걸 렸는지 보여줍니다 (적어도 나를 위해했던).
Justin Workman

마찬가지로 캐스케이드 600k 행을 삭제해야했으며 처음에는 100 % CPU 사용량으로 작업 당 2-10 사이를 차지했습니다. 이제 80 %의 CPU 사용량으로 모두 삭제하는 데 몇 분 밖에 걸리지 않았습니다.
fillobotto

어디에서나 외래 참조가있는 경우 소스 열에 실제 인덱스가 있어야합니다. 그렇지 않으면 성능이 저하됩니다. 나는 더 확실 경우는 아니지만 PRIMARY인덱스가 충분하지만, UNIQUE지수는 확실히이 목적을 위해 좋은 것만으로는 충분하지 않습니다.
Mikko Rantalainen

26

몇 가지 옵션이 있습니다. 가장 좋은 옵션은 트리거가 적중되지 않도록 일괄 삭제를 실행하는 것입니다. 삭제하기 전에 트리거를 비활성화 한 다음 다시 활성화하십시오. 이렇게하면 많은 시간이 절약됩니다. 예를 들면 다음과 같습니다.

ALTER TABLE tablename DISABLE TRIGGER ALL; 
DELETE ...; 
ALTER TABLE tablename ENABLE TRIGGER ALL;

여기서 중요한 열쇠는 하위 쿼리의 깊이를 최소화하려는 것입니다. 이 경우, 관련 정보를 저장하기 위해 임시 테이블을 설정하여 삭제시 서브 쿼리를 심하게 피할 수 있습니다.


제 경우에는 잠자리에 들기 전에 DELETE FROM 명령을 시작했는데 다음 날 컴퓨터로 돌아 왔을 때 여전히 수행되지 않았습니다. 한 번에 하나의 코어에서 100 % CPU 사용. 트리거를 비활성화하고 다시 시도한 후 200k 레코드를 삭제하는 데 3 초가 걸렸습니다. 고맙습니다!
Nick Woodhams

13

문제를 해결하는 가장 쉬운 방법은 PostgreSQL에서 자세한 타이밍을 쿼리하는 것 EXPLAIN입니다. 이를 위해서는 최소한 완료되지만 예상보다 오래 걸리는 단일 쿼리를 찾아야합니다. 이 줄이 다음과 같이 보일 것이라고 가정 해 봅시다.

delete from mydata where id='897b4dde-6a0d-4159-91e6-88e84519e6b6';

실제로 그 명령을 실행하는 대신 할 수 있습니다

begin;
explain (analyze,buffers,timing) delete from mydata where id='897b4dde-6a0d-4159-91e6-88e84519e6b6';
rollback;

결국 롤백을 수행하면 데이터베이스를 실제로 수정하지 않고도이를 실행할 수 있지만 여전히 소요 된 시간에 대한 자세한 타이밍을 얻을 수 있습니다. 이를 실행 한 후 출력에서 ​​일부 트리거로 인해 큰 지연이 발생할 수 있습니다.

...
Trigger for constraint XYZ123: time=12311.292 calls=1
...

time이 contraint을 확인하는 12.3 초 걸렸다 있도록 MS (밀리 초)입니다. INDEX이 트리거를 효과적으로 계산할 수 있도록 필요한 열에 새 항목을 추가해야합니다 . 외래 키 참조의 경우 다른 테이블을 참조하는 열 (대상 열이 아닌 소스 열)을 색인화해야합니다. PostgreSQL은 이러한 인덱스를 자동으로 생성하지 않으며 DELETE실제로 해당 인덱스가 필요한 유일한 일반적인 쿼리입니다. 결과적으로 DELETE인덱스 누락으로 인해 너무 느린 경우에 도달 할 때까지 수년간의 데이터가 누적되었을 수 있습니다 .

해당 제약 조건의 성능을 수정했거나 시간이 오래 걸린 다른 작업이 있으면 begin/ rollback블록 에서 명령을 반복 하여 새 실행 시간을 이전과 비교할 수 있습니다. 한 줄 삭제 응답 시간에 만족할 때까지 계속하십시오 (단순히 다른 인덱스를 추가하여 25.6 초에서 15 ms로 이동하는 쿼리가 하나 있습니다). 그런 다음 해킹없이 전체 삭제를 완료 할 수 있습니다.

( EXPLAIN성공적으로 완료 할 수있는 쿼리 가 필요합니다. PostgreSQL이 한 번의 삭제로 외래 키 제약 조건을 위반하고 EXPLAIN실패한 경우 타이밍을 방출하지 않기 때문에 사용할 수 없다는 것을 알아 내기 위해 너무 오래 걸린 문제가있었습니다. 이 경우 성능 문제를 쉽게 디버깅 할 수있는 방법을 모르겠습니다.)


8

트리거 비활성화는 DB 무결성에 위협이 될 수 있으므로 권장 할 수 없습니다. 그러나 작업이 제한 조건을 충족하지 못한다고 확신하면 다음과 같이 트리거를 비활성화 할 수 있습니다.SET session_replication_role = replica;

DELETE여기를 실행 하십시오.

트리거를 복원하려면 다음을 실행하십시오. SET session_replication_role = DEFAULT;

여기에 출처.


0

ON DELETE CASCADE 트리거가있는 경우, 이유가있을 수 있으므로이 기능을 사용하지 않아야합니다. 나를 위해 일하는 또 다른 트릭 (여전히 색인을 추가하십시오)은 계단식 끝에 테이블에서 시작하여 데이터를 수동으로 삭제하고 기본 테이블을 향해 작동하는 삭제 기능을 만드는 것입니다. (ON DELETE RESTRICT 트리거가있는 경우와 동일합니다)

CREATE TABLE tablea (
    tablea_uid integer
);

CREATE TABLE tableb (
    tableb_uid integer,
    tablea_rid integer REFERENCES tablea(tablea_uid)
);

CREATE TABLE tablec (
    tablec_uid integer,
    tableb_rid integer REFERENCES tableb(tableb_uid)
);

이 경우 tablec, tableb, tablea에서 데이터를 삭제하십시오.

CREATE OR REPLACE FUNCTION delete_in_order()
 RETURNS void AS $$

    DELETE FROM tablec;
    DELETE FROM tableb;
    DELETE FROM tablea;

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