Cách thay thế nguyên tử dữ liệu bảng trong PostgreSQL


14

Tôi muốn thay thế toàn bộ nội dung của một bảng, mà không ảnh hưởng đến bất kỳ SELECTcâu lệnh đến nào trong quá trình.

Trường hợp sử dụng là có một bảng lưu trữ thông tin hộp thư thường xuyên được trích xuất và cần được lưu trữ trong bảng PostgreQuery. Có nhiều khách hàng sử dụng một ứng dụng liên tục truy vấn cùng một bảng.

Thông thường, tôi sẽ làm một cái gì đó như (mã giả đến) ...

BEGIN TRANSACTION
TRUNCATE TABLE
INSERT INTO
COMMIT

Nhưng thật không may, bảng không thể được đọc trong quá trình này; do thời gian cần thiết INSERT INTOđể hoàn thành. Bàn bị khóa.

Trong MySQL, tôi đã sử dụng RENAME TABLElệnh nguyên tử của họ để tránh những vấn đề này ...

CREATE TABLE table_new LIKE table; 
INSERT INTO table_new;
RENAME TABLE table TO table_old, table_new TO table; *atomic operation*
DROP TABLE table_old;

Làm thế nào tôi có thể đạt được điều này trong PostgreSQL?

Đối với mục đích của câu hỏi này, bạn có thể cho rằng tôi không sử dụng khóa ngoại.


Tại sao bạn nghĩ rằng bảng không thể được đọc trong khi chèn các hàng trong đó? Bảng cắt ngắn sẽ có hiệu lực ngay lập tức trong tất cả các phiên; tuy nhiên, các phần chèn (nếu được thực hiện trong một giao dịch bao bọc tất cả chúng, như mã giả của bạn gợi ý) sẽ không hiển thị cho các phiên khác cho đến khi bạn cam kết. Các phiên khác sẽ có thể chọn từ bảng và sẽ thấy một bảng trống cho đến khi bạn cam kết.
zgguy

1
@zgguy TRUNCATElệnh sẽ có được khóa AccessExinating trên bảng, do đó, không ai khác có thể đọc từ bảng cho đến khi giao dịch đó được thực hiện hoặc được khôi phục.
Josh Kupershmidt

2
Nếu bạn sử dụng deletethay vì truncatenó sẽ chậm hơn, nhưng không chặn độc giả. Bạn cần xóa bao nhiêu hàng?
a_horse_with_no_name

@a_horse_with_no_name Thường nằm trong khoảng 200-300k hàng với nhiều cột varchar. Thời gian chờ đợi DELETEINSERTsẽ là quá lâu.
Clarkey

Câu trả lời:


20

Đúng, TRUNCATE TABLE lệnh bạn đang thực hiện "... có được khóa ĐỘC QUYỀN TIẾP CẬN trên mỗi bảng mà nó hoạt động ", vì vậy, trong khối SQL đầu tiên bạn đã đăng, mọi khách hàng khác cố gắng truy cập vào bảng sau thời gian đó sẽ bị chặn cho đến khi bạn INSERTkết thúc và bạn COMMIT.

Bạn có thể sử dụng cách giải quyết tương tự như trong mã dành riêng cho MySQL của bạn; Postgres hỗ trợ gần như cùng một cú pháp và sẽ có hành vi khóa tương tự. Để dí dỏm:

BEGIN;
-- You probably want to make sure that no one else is
-- INSERT / UPDATE / DELETE'ing from the original table, otherwise
-- those changes may be lost during this switchover process. One way
-- to do that would be via:
-- LOCK TABLE "table" IN ROW EXCLUSIVE mode;
CREATE TABLE "table_new" (LIKE "table");
INSERT INTO "table_new" ...;

-- The ALTER TABLE ... RENAME TO command takes an Access Exclusive lock on "table",
-- but these final few statements should be fast.
ALTER TABLE "table" RENAME TO "table_old";
ALTER TABLE "table_new" RENAME TO "table";
DROP TABLE "table_old";

COMMIT;

Phần thưởng thêm: Postgres thực sự hỗ trợ DDL giao dịch, không giống như MySQL, vì vậy trong trường hợp bạn cần ROLLBACK giao dịch trên, bạn có thể thực hiện một cách an toàn.


Tôi sẽ làm một số thử nghiệm về điều này, cảm ơn câu trả lời của bạn. Nếu tôi đã sử dụng LOCK TABLEphương pháp mà bạn đề xuất, tôi sẽ cần mở khóa lại trước khi COMMIT, hoặc nó sẽ tự mở khóa?
Clarkey

1
EDIT: Đã tìm thấy quy định sau trong tài liệu này : "Không có lệnh UNLOCK TABLE; các khóa luôn được giải phóng khi kết thúc giao dịch."
Clarkey

1
Một điều còn thiếu ở đây là tất cả các ràng buộc được đính kèm vẫn thuộc về_old
Intellix
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.