วิธีการแทนที่ข้อมูลตารางใน PostgreSQL


14

ฉันต้องการแทนที่เนื้อหาทั้งหมดของตารางโดยไม่กระทบกับSELECTคำสั่งที่เข้ามาในระหว่างกระบวนการ

กรณีการใช้งานคือการมีตารางที่เก็บข้อมูลกล่องจดหมายที่แตกเป็นประจำและจะต้องเก็บไว้ในตาราง PostgreSQL มีไคลเอนต์จำนวนมากที่ใช้แอปพลิเคชันที่สืบค้นตารางเดียวกันตลอดเวลา

โดยปกติฉันจะทำสิ่งที่ชอบ (pseudocode ขาเข้า) ...

BEGIN TRANSACTION
TRUNCATE TABLE
INSERT INTO
COMMIT

แต่น่าเสียดายที่ตารางไม่สามารถอ่านได้ในระหว่างกระบวนการนี้ เนื่องจากเวลาที่ใช้INSERT INTOในการทำให้เสร็จสมบูรณ์ ตารางถูกล็อค

ใน MySQL ฉันจะใช้RENAME TABLEคำสั่งatomic เพื่อหลีกเลี่ยงปัญหาเหล่านี้ ...

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;

ฉันจะบรรลุสิ่งนี้ใน PostgreSQL ได้อย่างไร

สำหรับวัตถุประสงค์ของคำถามนี้คุณสามารถสมมติว่าฉันไม่ได้ใช้กุญแจต่างประเทศ


ทำไมคุณถึงคิดว่าตารางไม่สามารถอ่านได้ในขณะที่แทรกแถวเข้าไป ตารางการตัดปลายจะมีผลทันทีในทุกเซสชัน อย่างไรก็ตามส่วนแทรก (หากทำในการทำธุรกรรมที่ล้อมรอบพวกเขาทั้งหมดตามรหัสหลอกของคุณแนะนำ) จะไม่ปรากฏให้เห็นในเซสชันอื่นจนกว่าคุณจะยอมรับ ส่วนอื่น ๆ จะสามารถเลือกจากตารางและจะเห็นตารางว่างเปล่าจนกว่าคุณจะยอมรับ
zgguy

2
@zgguy TRUNCATEคำสั่งจะรับการล็อก AccessExclusive บนโต๊ะดังนั้นจะไม่มีใครสามารถอ่านจากตารางได้จนกว่าธุรกรรมนั้นจะทำหรือย้อนกลับ
Josh Kupershmidt

2
หากคุณใช้deleteแทนtruncateมันจะช้าลง แต่ไม่มีการปิดกั้นผู้อ่าน คุณต้องลบกี่แถว
a_horse_with_no_name

@a_horse_with_no_name โดยปกติแล้วจะอยู่ระหว่าง 200-300k แถวที่มีคอลัมน์ varchar จำนวนมาก เวลารอของDELETEและINSERTจะนานเกินไป
Clarkey

คำตอบ:


21

ขวาTRUNCATE TABLE คำสั่งที่คุณกำลังดำเนินการ "... ได้รับการล็อคการเข้าถึงแบบเอกสิทธิ์เฉพาะบุคคลบนแต่ละตารางที่ทำงาน " ดังนั้นในบล็อก SQL แรกที่คุณโพสต์ไคลเอนต์อื่น ๆ ที่พยายามเข้าถึงตารางหลังจากเวลานั้นจะถูกบล็อกจนกว่าจะINSERTเสร็จสิ้น COMMITและคุณ

คุณสามารถใช้วิธีแก้ปัญหาแบบเดียวกับในรหัส MySQL ของคุณโดยเฉพาะ Postgres รองรับไวยากรณ์เดียวกันโดยประมาณและจะมีลักษณะการล็อคคล้ายกัน เพื่อปัญญา:

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;

โบนัสพิเศษ: Postgres รองรับ DDL ของธุรกรรมจริงไม่เหมือนกับ MySQL ดังนั้นในกรณีที่คุณต้องการย้อนกลับการทำธุรกรรมข้างต้นคุณอาจทำได้อย่างปลอดภัย


ฉันจะทำการทดสอบนี้ขอบคุณสำหรับคำตอบของคุณ หากฉันใช้LOCK TABLEวิธีที่คุณแนะนำฉันจะต้องปลดล็อกอีกครั้งก่อนหน้าCOMMITหรือจะปลดล็อคตัวเองหรือไม่
Clarkey

1
แก้ไข: พบ statment ต่อไปนี้ในเอกสารนี้ : "ไม่มีคำสั่ง UNLOCK TABLE; การล็อกจะถูกปล่อยเมื่อสิ้นสุดการทำธุรกรรมเสมอ"
Clarkey

2
สิ่งหนึ่งที่ขาดหายไปที่นี่คือข้อ จำกัด ที่แนบมาซึ่งยังคงเป็นของ_old
Intellix

@Intellix คุณสามารถทำอย่างละเอียดเกี่ยวกับว่า? หมายความว่าข้อ จำกัด นั้นตั้งชื่อตามตารางเก่าหรือว่าเกี่ยวข้องกับตารางเก่าเท่านั้น (หมายถึงข้อ จำกัด ถูกทิ้งอย่างมีประสิทธิภาพ)?
maerics

ความคิดเห็นก่อนการสร้างตาราง ( -- LOCK TABLE "table" IN ROW EXCLUSIVE mode;) ดูเหมือนจะไม่เพียงพอที่จะป้องกันจากการปรับปรุง / แทรกลงในตารางต้นฉบับตามรายละเอียด ROW EXCLUSIVEสามารถล็อกสองรายการได้โดยไม่มีข้อขัดแย้งใด ๆ (ดูตารางที่ 13.2 ในpostgresql.org/docs/10/explicit-locking.html#LOCKING-TABLES ) เพื่อป้องกันการอัพเดทข้อมูลคุณต้องSHAREล็อคอย่างน้อย
Pilou
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.