Bagaimana cara mengganti data tabel secara atom dalam PostgreSQL

14

Saya ingin mengganti seluruh isi tabel, tanpa memengaruhi SELECTpernyataan yang masuk selama proses.

Kasus penggunaan adalah memiliki tabel yang menyimpan informasi kotak surat yang diekstraksi secara teratur, dan perlu disimpan dalam tabel PostgreSQL. Ada banyak klien yang menggunakan aplikasi yang terus-menerus menanyakan tabel yang sama.

Biasanya, saya akan melakukan sesuatu seperti (pseudocode incoming) ...

BEGIN TRANSACTION
TRUNCATE TABLE
INSERT INTO
COMMIT

Namun sayangnya tabel tidak dapat dibaca selama proses ini; karena waktu yang dibutuhkan INSERT INTOuntuk menyelesaikan. Meja dikunci.

Di MySQL, saya akan menggunakan RENAME TABLEperintah atom mereka untuk menghindari masalah ini ...

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;

Bagaimana saya bisa mencapai ini di PostgreSQL?

Untuk keperluan pertanyaan ini, Anda dapat menganggap saya tidak menggunakan kunci asing.

Clarkey
sumber
Mengapa Anda berpikir bahwa tabel tidak dapat dibaca saat memasukkan baris di dalamnya? Tabel terpotong akan memiliki efek langsung di semua sesi; namun, sisipan (jika dilakukan di dalam transaksi yang membungkus semuanya, seperti yang disarankan oleh kode semu Anda) tidak akan terlihat oleh sesi lain sampai Anda berkomitmen. Sesi lain akan dapat memilih dari tabel, dan akan melihat tabel kosong sampai Anda komit.
zgguy
2
@zgguy TRUNCATEperintah akan mendapatkan kunci AccessExclusive di atas meja, jadi tidak ada orang lain yang bisa membaca dari tabel sampai transaksi itu dilakukan atau dibatalkan.
Josh Kupershmidt
2
Jika Anda menggunakan deletebukannya truncateitu akan lebih lambat, tapi tanpa pembaca menghalangi. Berapa banyak baris yang perlu Anda hapus?
a_horse_with_no_name
@a_horse_with_no_name Biasanya antara 200-300 ribu baris dengan banyak kolom varchar. Waktu tunggu DELETEdan INSERTakan terlalu lama.
Clarkey

Jawaban:

20

Benar, TRUNCATE TABLE perintah yang Anda jalankan "... memperoleh kunci AKSES EKSKLUSIF pada setiap tabel tempat operasinya ", jadi pada blok SQL pertama yang Anda pasang, setiap klien lain yang mencoba mengakses tabel setelah waktu tersebut akan diblokir hingga Anda INSERTselesai dan Anda COMMIT.

Anda dapat menggunakan solusi yang sama seperti pada kode khusus MySQL Anda; Postgres mendukung kira-kira sintaks yang sama dan akan memiliki perilaku penguncian yang serupa. Yakni:

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

Bonus tambahan: Postgres sebenarnya mendukung DDL transaksional, tidak seperti MySQL, jadi jika Anda perlu ROLLBACK transaksi di atas, Anda dapat melakukannya dengan aman.

Josh Kupershmidt
sumber
Saya akan melakukan beberapa pengujian tentang ini, terima kasih atas jawaban Anda. Jika saya menggunakan LOCK TABLEmetode yang Anda sarankan, apakah saya perlu membuka kunci lagi sebelum COMMIT, atau akankah itu membuka kunci sendiri?
Clarkey
1
EDIT: Ditemukan statment berikut dalam dokumentasi ini : "Tidak ada perintah UNLOCK TABLE; kunci selalu dirilis pada akhir transaksi."
Clarkey
2
Satu hal yang hilang di sini adalah semua kendala yang masih ada_old
Intellix
@Intellix dapatkah Anda menguraikannya? Apakah itu berarti bahwa kendala hanya dinamai per tabel lama atau bahwa mereka hanya berkaitan dengan tabel lama (artinya kendala tersebut secara efektif dihapus)?
maerics
Komentar sebelum pembuatan tabel ( -- LOCK TABLE "table" IN ROW EXCLUSIVE mode;) tampaknya tidak memadai untuk melindungi dari pembaruan / masukkan ke tabel sumber sesuai dengan spesifikasi. Dua ROW EXCLUSIVEkunci dapat diperoleh tanpa konflik apa pun (lihat Tabel 13.2 di postgresql.org/docs/10/explicit-locking.html#LOCKING-TABLES ). Untuk mencegah pembaruan data, Anda memerlukan setidaknya SHAREkunci.
Pilou