Tối ưu hóa hiệu suất cập nhật hàng loạt trong PostgreSQL


37

Sử dụng PG 9.1 trên Ubuntu 12.04.

Hiện tại chúng tôi mất tới 24 giờ để chạy một tập hợp lớn các câu lệnh CẬP NHẬT trên cơ sở dữ liệu, có dạng:

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

(Chúng tôi chỉ ghi đè các trường của các đối tượng được xác định bởi ID.) Các giá trị đến từ nguồn dữ liệu ngoài (chưa có trong DB trong bảng).

Các bảng có rất nhiều chỉ số mỗi và không có ràng buộc khóa ngoại. Không có CAM KẾT được thực hiện cho đến cuối cùng.

Phải mất 2h để nhập a pg_dumpcủa toàn bộ DB. Điều này có vẻ như một đường cơ sở chúng ta nên nhắm mục tiêu hợp lý.

Thiếu việc tạo ra một chương trình tùy chỉnh bằng cách nào đó tái tạo lại một tập dữ liệu cho PostgreSQL để nhập lại, liệu chúng ta có thể làm gì để mang lại hiệu năng CẬP NHẬT hàng loạt gần hơn với việc nhập? (Đây là một khu vực mà chúng tôi tin rằng cây hợp nhất có cấu trúc log xử lý tốt, nhưng chúng tôi tự hỏi liệu có bất cứ điều gì chúng tôi có thể làm trong PostgreQuery không.)

Một vài ý tưởng:

  • bỏ tất cả các chỉ số không ID và xây dựng lại sau đó?
  • tăng checkpoint_segments, nhưng điều này có thực sự giúp duy trì thông lượng dài hạn không?
  • sử dụng các kỹ thuật được đề cập ở đây ? (Tải dữ liệu mới dưới dạng bảng, sau đó "hợp nhất" dữ liệu cũ nơi không tìm thấy ID trong dữ liệu mới)

Về cơ bản có rất nhiều thứ để thử và chúng tôi không chắc những gì hiệu quả nhất hoặc nếu chúng tôi đang xem những thứ khác. Chúng tôi sẽ dành vài ngày tới để thử nghiệm, nhưng chúng tôi nghĩ rằng chúng tôi cũng sẽ hỏi ở đây.

Tôi có tải đồng thời trên bàn nhưng nó chỉ đọc.


Thông tin quan trọng bị thiếu trong câu hỏi của bạn: Phiên bản Postgres của bạn? Các giá trị đến từ đâu? Âm thanh như một tập tin bên ngoài cơ sở dữ liệu, nhưng xin vui lòng làm rõ. Bạn có tải đồng thời trên bảng đích không? Nếu có, chính xác là gì? Hoặc bạn có thể đủ khả năng để thả và tái tạo? Không có khóa ngoại, ok - nhưng có các đối tượng phụ thuộc khác như chế độ xem không? Vui lòng chỉnh sửa câu hỏi của bạn với thông tin còn thiếu. Đừng ép nó trong một bình luận.
Erwin Brandstetter

@ErwinBrandstetter Cảm ơn, đã cập nhật câu hỏi của tôi.
Dương

Tôi cho rằng bạn đã kiểm tra thông qua explain analyzeviệc sử dụng chỉ mục để tra cứu?
rogerdpack

Câu trả lời:


45

Giả định

Vì thông tin bị thiếu trong Q, tôi sẽ giả sử:

  • Dữ liệu của bạn đến từ một tệp trên máy chủ cơ sở dữ liệu.
  • Dữ liệu được định dạng giống như COPYđầu ra, với một hàng duy nhất id để khớp với bảng đích.
    Nếu không, định dạng đúng trước hoặc sử dụng COPYcác tùy chọn để xử lý định dạng.
  • Bạn đang cập nhật từng hàng trong bảng mục tiêu hoặc hầu hết trong số chúng.
  • Bạn có thể đủ khả năng để thả và tạo lại bảng mục tiêu.
    Điều đó có nghĩa là không có quyền truy cập đồng thời. Khác xem xét câu trả lời liên quan này:
  • Không có đối tượng phụ thuộc nào cả, ngoại trừ các chỉ số.

Dung dịch

Tôi đề nghị bạn nên thực hiện một cách tiếp cận tương tự như được nêu tại liên kết từ viên đạn thứ ba của bạn . Với sự tối ưu hóa lớn.

Để tạo bảng tạm thời, có một cách đơn giản và nhanh hơn:

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

Một lớn duy nhất UPDATEtừ một bảng tạm thời bên trong cơ sở dữ liệu sẽ nhanh hơn các cập nhật riêng lẻ từ bên ngoài cơ sở dữ liệu theo một số bậc độ lớn.

Trong mô hình MVCC của PostgreQuery , một UPDATEphương tiện để tạo một phiên bản hàng mới và đánh dấu phiên bản cũ là đã xóa. Đó là khoảng đắt như một INSERTDELETEkết hợp. Thêm vào đó, nó để lại cho bạn rất nhiều tuple chết. Vì dù sao bạn cũng đang cập nhật toàn bộ bảng, nên sẽ nhanh hơn nếu chỉ tạo một bảng mới và bỏ bảng cũ.

Nếu bạn có đủ RAM, hãy đặt temp_buffers(chỉ cho phiên này!) Đủ cao để giữ bảng tạm thời trong RAM - trước khi bạn làm bất cứ điều gì khác.

Để có ước tính cần bao nhiêu RAM, hãy chạy thử nghiệm với một mẫu nhỏ và sử dụng các hàm kích thước đối tượng db :

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

Kịch bản hoàn chỉnh

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

Tải đồng thời

Các hoạt động đồng thời trên bảng (mà tôi đã loại trừ trong các giả định khi bắt đầu) sẽ chờ, một khi bảng bị khóa gần cuối và thất bại ngay khi giao dịch được thực hiện, vì tên bảng được giải quyết ngay lập tức cho OID của nó, nhưng bảng mới có OID khác. Bảng vẫn nhất quán, nhưng các hoạt động đồng thời có thể có một ngoại lệ và phải được lặp lại. Chi tiết trong câu trả lời liên quan này:

CẬP NHẬT lộ trình

Nếu bạn (phải) đi theo UPDATElộ trình, hãy bỏ bất kỳ chỉ mục nào không cần thiết trong quá trình cập nhật và tạo lại nó sau đó. Nó rẻ hơn nhiều để tạo một chỉ mục trong một mảnh so với cập nhật nó cho mỗi hàng riêng lẻ. Điều này cũng có thể cho phép cập nhật NÓNG .

Tôi đã phác thảo một quy trình tương tự bằng cách sử dụng UPDATEtrong câu trả lời liên quan chặt chẽ này trên SO .

 


1
Tôi thực sự chỉ cập nhật 20% số hàng trong bảng mục tiêu - không phải tất cả, nhưng một phần đủ lớn để hợp nhất có thể tốt hơn so với tìm kiếm cập nhật ngẫu nhiên.
Dương

1
@AryehLeibTaurog: Điều đó không nên xảy ra kể từ khi DROP TABLElấy ra một Access Exclusive Lock. Dù bằng cách nào, tôi đã liệt kê điều kiện tiên quyết ở đầu câu trả lời của mình: You can afford to drop and recreate the target table.Nó có thể giúp khóa bảng khi bắt đầu giao dịch. Tôi đề nghị bạn bắt đầu một câu hỏi mới với tất cả các chi tiết có liên quan về tình huống của bạn để chúng tôi có thể đi đến tận cùng của vấn đề này.
Erwin Brandstetter

1
@ErwinBrandstetter Thú vị. Nó dường như phụ thuộc vào phiên bản máy chủ. Tôi đã sao chép lỗi trên 8.4 và 9.1 bằng bộ điều hợp psycopg2sử dụng máy khách psql . Ngày 9.3 không có lỗi. Xem ý kiến ​​của tôi trong kịch bản đầu tiên. Tôi không chắc chắn nếu có một câu hỏi để đăng ở đây, nhưng nó có thể đáng để thu hút một số thông tin về một trong các danh sách postgresql.
Aryeh Leib Taurog

1
Tôi đã viết một lớp trợ giúp đơn giản trong python để tự động hóa quá trình.
Aryeh Leib Taurog

3
Câu trả lời rất hữu ích. Là một biến thể nhỏ, người ta có thể tạo bảng tạm thời chỉ có các cột được cập nhật và các cột tham chiếu, xóa các cột được cập nhật từ bảng gốc, sau đó hợp nhất các bảng bằng cách sử dụng CREATE TABLE tbl_new AS SELECT t.*, u.field1, u.field2 from tbl t NATURAL LEFT JOIN tmp_tbl u;, LEFT JOINcho phép giữ các hàng không có cập nhật. Tất nhiên NATURALcó thể được thay đổi thành bất kỳ hợp lệ USING()hoặc ON.
Skippy le Grand Gourou

2

Nếu dữ liệu có thể được cung cấp trong một tệp có cấu trúc, bạn có thể đọc nó với một trình bao bọc dữ liệu nước ngoài và thực hiện hợp nhất trên bảng đích.


3
Bạn có ý nghĩa cụ thể bằng cách "hợp nhất trên bảng mục tiêu"? Tại sao sử dụng FDW tốt hơn so với SAO CHÉP vào bảng tạm thời (như được đề xuất trong dấu đầu dòng thứ ba trong câu hỏi ban đầu)?
Dương

"Hợp nhất" như trong câu lệnh MERGE sql. Sử dụng FDW cho phép bạn làm điều đó mà không cần thêm bước sao chép dữ liệu vào một bảng tạm thời. Tôi giả sử rằng bạn không thay thế toàn bộ tập dữ liệu và sẽ có một lượng dữ liệu nhất định trong tệp không thể hiện thay đổi so với tập dữ liệu hiện tại - nếu một lượng đáng kể đã thay đổi thì hoàn thành thay thế bảng có thể là giá trị.
David Aldridge

1
@DavidAldridge: Mặc dù được định nghĩa trong tiêu chuẩn SQL: 2003, nhưng MERGEkhông được triển khai trong PostgreQuery (chưa). Các triển khai trong RDBMS khác thay đổi khá nhiều. Xem xét thông tin thẻ cho MERGEUPSERT.
Erwin Brandstetter

@ErwinBrandstetter [glurk] Ồ vâng, đúng vậy. Vâng Merge là đóng băng trên bánh thực sự tôi cho rằng. Truy cập dữ liệu mà không cần bước nhập bảng tạm thời thực sự là mấu chốt của kỹ thuật FDW.
David Aldridge
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.