XÓA rất chậm trong PostgreSQL, cách giải quyết?


30

Tôi có một cơ sở dữ liệu trên PostgreSQL 9.2 có một lược đồ chính với khoảng 70 bảng và một số lượng khác nhau của các lược đồ cho mỗi khách hàng có cấu trúc giống nhau gồm 30 bảng mỗi bảng. Các lược đồ máy khách có các khóa ngoại tham chiếu lược đồ chính và không phải là cách khác.

Tôi mới bắt đầu điền vào cơ sở dữ liệu với một số dữ liệu thực được lấy từ phiên bản trước. DB đã đạt khoảng 1,5 GB (dự kiến ​​sẽ tăng lên vài GB trong vài tuần) khi tôi phải xóa hàng loạt trong một bảng rất trung tâm trong lược đồ chính. Tất cả các khóa ngoại liên quan được đánh dấu TRÊN XÓA CASCADE.

Không có gì ngạc nhiên khi việc này sẽ mất nhiều thời gian nhưng sau 12 giờ, rõ ràng là tôi nên bắt đầu lại từ đầu, bỏ DB và khởi động lại quá trình di chuyển. Nhưng nếu tôi cần lặp lại thao tác này sau khi DB còn sống và lớn hơn nhiều thì sao? Có phương pháp thay thế, nhanh hơn?

Sẽ nhanh hơn nhiều nếu tôi viết một tập lệnh sẽ duyệt các bảng phụ thuộc, bắt đầu từ bảng xa nhất từ ​​bảng trung tâm, xóa các bảng phụ thuộc theo bảng?

Một chi tiết quan trọng là có các kích hoạt trên một số bảng.


4
Sau 5 năm, tôi đang thay đổi câu trả lời được chấp nhận. XÓA chậm hầu như luôn luôn gây ra bởi các chỉ mục bị thiếu trên các khóa ngoại có tham chiếu trực tiếp hoặc gián tiếp đến bảng bị xóa. Các trình kích hoạt kích hoạt các câu lệnh XÓA cũng có thể làm chậm mọi thứ, mặc dù giải pháp hầu như luôn luôn là làm cho chúng chạy nhanh hơn (ví dụ bằng cách thêm các chỉ mục bị thiếu) và gần như không bao giờ tắt tất cả các kích hoạt.
jd.

Câu trả lời:


30

Tôi đã có một vấn đề tương tự. Hóa ra, những ON DELETE CASCADEyếu tố kích hoạt đó đã làm mọi thứ chậm lại khá nhiều, bởi vì những lần xóa tầng đó rất chậm.

Tôi đã giải quyết vấn đề bằng cách tạo các chỉ mục trên các trường khóa ngoại trên các bảng tham chiếu và tôi đã đi từ mất hàng giờ để xóa đến vài giây.


Ồ, điều này đã giúp tôi xóa các bản ghi 8 triệu trong vài phút. Nhưng điều tôi không hiểu là bảng của tôi chỉ chứa các tham chiếu đến các bảng khác, không có bảng nào khác giữ các tham chiếu đến bảng của tôi. Vậy chính xác thì hiệu ứng ở đây là gì? (Tôi không sử dụng ON DELETE CASCADE)
msrd0

2
Điều này đã giải quyết nó cho tôi là tốt. Đối với bất kỳ ai đang thử điều này, bạn có thể thực hiện một EXPLAIN (ANALYZE, BUFFERS)truy vấn trên một hàng xóa và nó sẽ cho bạn thấy các ràng buộc khóa ngoại nào mất nhiều thời gian nhất (ít nhất là nó đã làm cho tôi).
Justin Workman

Tương tự, phải xóa trên hàng 600 nghìn hàng và lúc đầu, nó chỉ mất từ ​​2-10 cho mỗi thao tác với mức sử dụng CPU 100%. Bây giờ chỉ mất vài phút để xóa tất cả chúng với 80% sử dụng CPU.
fillobotto

Điều quan trọng cần lưu ý là nếu bạn có một tài liệu tham khảo nước ngoài đến bất cứ nơi nào, cột nguồn phải có chỉ số thực hoặc hiệu suất sẽ bị ảnh hưởng. Tôi không chắc PRIMARYchỉ số có đủ không nhưng UNIQUEchỉ số chắc chắn không đủ tốt cho mục đích này.
Mikko Rantalainen

26

Bạn có một vài lựa chọn. Tùy chọn tốt nhất là chạy xóa hàng loạt để kích hoạt không bị tấn công. Vô hiệu hóa các kích hoạt trước khi xóa, sau đó kích hoạt lại chúng. Điều này giúp bạn tiết kiệm một lượng thời gian rất lớn. Ví dụ:

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

Một chìa khóa quan trọng ở đây là bạn muốn giảm thiểu độ sâu của các truy vấn con. Trong trường hợp này, bạn có thể muốn thiết lập các bảng tạm thời để lưu trữ thông tin liên quan để bạn có thể tránh các truy vấn con sâu khi xóa.


Trong trường hợp của tôi, tôi đã bắt đầu lệnh XÓA TỪ trước khi đi ngủ và nó vẫn không được thực hiện khi tôi trở lại máy tính của mình vào ngày hôm sau. 100% CPU sử dụng trên một lõi toàn bộ thời gian. Sau khi vô hiệu hóa các kích hoạt và thử lại, phải mất 3 giây để xóa 200k hồ sơ. Cảm ơn bạn!
Nick Woodhams

13

Phương pháp dễ nhất để giải quyết vấn đề là truy vấn thời gian chi tiết từ PostgreSQL: EXPLAIN . Đối với điều này, bạn cần tìm tối thiểu một truy vấn duy nhất hoàn thành nhưng mất nhiều thời gian hơn dự kiến. Hãy nói rằng dòng này sẽ giống như

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

Thay vì thực sự chạy lệnh đó, bạn có thể làm

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

Việc khôi phục cuối cùng cho phép chạy nó mà không thực sự sửa đổi cơ sở dữ liệu nhưng bạn vẫn có được thời gian chi tiết của những gì đã mất bao nhiêu. Sau khi chạy nó, bạn có thể thấy trong đầu ra một số kích hoạt gây ra sự chậm trễ lớn:

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

Thời gian tính timebằng ms (mili giây) nên việc kiểm tra phần tiếp giáp này mất khoảng 12,3 giây. Bạn cần thêm một cái mớiINDEX cột qua các cột cần thiết để kích hoạt này có thể được tính toán hiệu quả. Đối với tham chiếu khóa ngoài, cột tham chiếu đến bảng khác phải được lập chỉ mục (nghĩa là cột nguồn, không phải cột mục tiêu). PostgreSQL không tự động tạo các chỉ mục như vậy cho bạn và DELETElà truy vấn phổ biến duy nhất mà bạn thực sự cần chỉ mục đó. Do đó, bạn có thể đã tích lũy nhiều năm dữ liệu cho đến khi bạn gặp trường hợp DELETEquá chậm do thiếu chỉ mục.

Khi bạn đã cố định hiệu suất của ràng buộc đó (hoặc một số thứ khác mất quá nhiều thời gian), hãy lặp lại lệnh trong begin / rollbackblock để bạn có thể so sánh thời gian thực hiện mới với trước đó. Tiếp tục cho đến khi bạn hài lòng với thời gian phản hồi xóa một dòng (Tôi có một truy vấn để đi từ 25,6 giây đến 15 ms chỉ bằng cách thêm các chỉ mục khác nhau). Sau đó, bạn có thể tiến hành hoàn thành xóa hoàn toàn mà không cần hack.

(Lưu ý rằng EXPLAINcần một truy vấn có thể hoàn thành thành công. Tôi đã từng gặp sự cố khi PostgreQuery mất quá nhiều thời gian để nhận ra rằng một lần xóa sẽ vi phạm ràng buộc khóa ngoại và trong trường hợp đó EXPLAINkhông thể sử dụng được vì nó sẽ không phát ra thời gian cho thất bại truy vấn. Tôi không biết cách dễ dàng để gỡ lỗi các vấn đề về hiệu năng trong trường hợp như vậy.)


8

Vô hiệu hóa kích hoạt có thể là một mối đe dọa đối với tính toàn vẹn DB và không thể được khuyến nghị; tuy nhiên nếu bạn chắc chắn rằng hoạt động của bạn là bằng chứng không ràng buộc, bạn có thể vô hiệu hóa các kích hoạt, với những điều sau đây:SET session_replication_role = replica;

Chạy DELETEở đây.

Để khôi phục kích hoạt, hãy chạy: SET session_replication_role = DEFAULT;

Nguồn ở đây.


0

Nếu bạn có các kích hoạt BẬT XÓA CASCADE, họ hy vọng có lý do, và do đó không nên bị vô hiệu hóa. Một mẹo khác (vẫn thêm các chỉ mục của bạn) hoạt động với tôi là tạo một hàm xóa xóa thủ công dữ liệu bắt đầu với các bảng ở cuối tầng và hoạt động về phía bảng chính. (Điều này giống như bạn sẽ phải làm nếu bạn có trình kích hoạt BẬT XÓA BẮT ĐẦU)

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)
);

Trong trường hợp này, xóa dữ liệu trong tablec rồi đến bảng và sau đó là tablea

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

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

$$ LANGUAGE SQL;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.