วิธีการแบ่งพาร์ติชันตารางที่มีอยู่ใน postgres


19

ฉันต้องการพาร์ติชันตารางที่มี 1M + แถวตามช่วงวันที่ วิธีนี้ทำได้โดยไม่ต้องหยุดทำงานหรือเสี่ยงต่อการสูญเสียข้อมูล นี่คือกลยุทธ์ที่ฉันกำลังพิจารณา แต่เปิดรับข้อเสนอแนะ:

  1. ตารางที่มีอยู่คือต้นแบบและลูก ๆ สืบทอดมาจากตาราง เมื่อเวลาผ่านไปย้ายข้อมูลจากต้นแบบไปยังเด็ก แต่จะมีช่วงเวลาที่ข้อมูลบางอย่างอยู่ในตารางหลักและบางส่วนในเด็ก

  2. สร้างตารางหลักและตารางลูกใหม่ สร้างสำเนาของข้อมูลในตารางที่มีอยู่ในตารางลูก (ดังนั้นข้อมูลจะอยู่ในที่สองแห่ง) เมื่อตารางลูกมีข้อมูลล่าสุดให้เปลี่ยนส่วนแทรกทั้งหมดไปข้างหน้าเพื่อชี้ไปที่ตารางต้นแบบใหม่และลบตารางที่มีอยู่


1
นี่คือแนวคิดของฉัน: ถ้าตารางมีคอลัมน์วันที่และเวลา -> สร้างต้นแบบใหม่ + ลูกใหม่ -> แทรกข้อมูลใหม่ไปยัง NEW + OLD (เช่น: datetime = 2015-07-06 00:00:00) -> คัดลอกจาก OLD ไปยังฐานใหม่ ตรงเวลาคอลัมน์ (ที่ไหน: datetime <2015-07-06 00:00:00) -> เปลี่ยนชื่อตาราง -> เปลี่ยนการแทรกเป็นใหม่อื่น -> สร้าง "พาร์ทิชันทริกเกอร์" เพื่อแทรก / อัปเดตบนต้นแบบ (แทรก / อัปเดตข้อมูลใหม่ - > ย้ายไปยัง childs ดังนั้นข้อมูลใหม่จะถูกแทรกไปยัง childs) -> update master ทริกเกอร์จะย้ายข้อมูลไปยัง childs
Luan Huynh

@Innhh ดังนั้นคุณแนะนำตัวเลือกที่สอง แต่เมื่อคัดลอกข้อมูลแล้วให้ลบตารางเก่าแล้วเปลี่ยนชื่อตารางใหม่เพื่อให้มีชื่อเหมือนกับตารางเก่า นั่นถูกต้องใช่ไหม?
Evan Appleby

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

2
เพียงไม่กี่ล้านแถวฉันไม่คิดว่าการแบ่งเป็นสิ่งจำเป็น ทำไมคุณคิดว่าคุณต้องการมัน? คุณพยายามแก้ไขปัญหาอะไร
a_horse_with_no_name

1
@EvanAppleby DELETE FROM ONLY master_tableเป็นวิธีแก้ปัญหา
dezso

คำตอบ:


21

เนื่องจาก # 1 ต้องการคัดลอกข้อมูลจากต้นแบบไปยังเด็กในขณะที่อยู่ในสภาพแวดล้อมการผลิตที่ใช้งานอยู่ฉันจึงไปกับ # 2 (การสร้างต้นแบบใหม่) สิ่งนี้จะช่วยป้องกันการหยุดชะงักของตารางดั้งเดิมในขณะที่กำลังใช้งานอยู่และหากมีปัญหาใด ๆ ฉันสามารถลบต้นแบบใหม่ได้อย่างง่ายดายโดยไม่มีปัญหาและใช้ตารางเดิมต่อไป นี่คือขั้นตอนที่ต้องทำ:

  1. สร้างตารางต้นแบบใหม่

    CREATE TABLE new_master (
        id          serial,
        counter     integer,
        dt_created  DATE DEFAULT CURRENT_DATE NOT NULL
    );
  2. สร้างลูกที่สืบทอดมาจากอาจารย์

    CREATE TABLE child_2014 (
        CONSTRAINT pk_2014 PRIMARY KEY (id),
        CONSTRAINT ck_2014 CHECK ( dt_created < DATE '2015-01-01' )
    ) INHERITS (new_master);
    CREATE INDEX idx_2014 ON child_2014 (dt_created);
    
    CREATE TABLE child_2015 (
        CONSTRAINT pk_2015 PRIMARY KEY (id),
        CONSTRAINT ck_2015 CHECK ( dt_created >= DATE '2015-01-01' AND dt_created < DATE '2016-01-01' )
    ) INHERITS (new_master);
    CREATE INDEX idx_2015 ON child_2015 (dt_created);
    
    ...
  3. คัดลอกข้อมูลประวัติทั้งหมดไปยังตารางต้นแบบใหม่

    INSERT INTO child_2014 (id,counter,dt_created)
    SELECT id,counter,dt_created
    from old_master
    where dt_created < '01/01/2015'::date;
  4. หยุดชั่วคราวแทรก / ปรับปรุงใหม่ไปยังฐานข้อมูลการผลิตชั่วคราว

  5. คัดลอกข้อมูลล่าสุดไปยังตารางต้นแบบใหม่

    INSERT INTO child_2015 (id,counter,dt_created)
    SELECT id,counter,dt_created
    from old_master
    where dt_created >= '01/01/2015'::date AND dt_created < '01/01/2016'::date;
  6. เปลี่ยนชื่อตารางเพื่อให้ new_master กลายเป็นฐานข้อมูลการผลิต

    ALTER TABLE old_master RENAME TO old_master_backup;
    ALTER TABLE new_master RENAME TO old_master;
  7. เพิ่มฟังก์ชันสำหรับคำสั่ง INSERT ใน old_master เพื่อให้ข้อมูลได้รับการส่งผ่านไปยังพาร์ติชันที่ถูกต้อง

    CREATE OR REPLACE FUNCTION fn_insert() RETURNS TRIGGER AS $$
    BEGIN
        IF ( NEW.dt_created >= DATE '2015-01-01' AND
             NEW.dt_created < DATE '2016-01-01' ) THEN
            INSERT INTO child_2015 VALUES (NEW.*);
        ELSIF ( NEW.dt_created < DATE '2015-01-01' ) THEN
            INSERT INTO child_2014 VALUES (NEW.*);
        ELSE
            RAISE EXCEPTION 'Date out of range';
        END IF;
        RETURN NULL;
    END;
    $$
    LANGUAGE plpgsql;
  8. เพิ่มทริกเกอร์เพื่อให้ฟังก์ชั่นนั้นถูกเรียกใช้บน INSERTS

    CREATE TRIGGER tr_insert BEFORE INSERT ON old_master
    FOR EACH ROW EXECUTE PROCEDURE fn_insert();
  9. ตั้งค่าการ จำกัด ข้อ จำกัด เป็น ON

    SET constraint_exclusion = on;
  10. เปิดใช้งานการปรับปรุงและการแทรกบนฐานข้อมูลการผลิตอีกครั้ง

  11. ตั้งค่าทริกเกอร์หรือ cron เพื่อให้พาร์ติชันใหม่ถูกสร้างขึ้นและฟังก์ชั่นได้รับการอัพเดตเพื่อกำหนดข้อมูลใหม่ให้กับพาร์ติชันที่ถูกต้อง อ้างอิงบทความนี้เพื่อดูตัวอย่างโค้ด

  12. ลบ old_master_backup


1
เขียนดี มันน่าสนใจถ้านั่นทำให้การสืบค้นของคุณเร็วขึ้น 10 ล้านยังคงไม่มากที่ฉันจะคิดเกี่ยวกับการแบ่ง ฉันสงสัยว่าประสิทธิภาพการลดระดับของคุณอาจเกิดจากการvacuumไม่ติดขัดหรือถูกป้องกันเนื่องจากเซสชัน "ไม่ทำงานในธุรกรรม"
a_horse_with_no_name

@a_horse_with_no_name เพื่อให้ห่างไกลมันไม่ได้ทำแบบสอบถามที่ดีขึ้นอย่างมีนัยสำคัญ :( ผมใช้ Heroku ซึ่งมีการตั้งค่าอัตโนมัติสูญญากาศและดูเหมือนว่ามันจะเกิดขึ้นในชีวิตประจำวันสำหรับตารางขนาดใหญ่นี้จะดูมากยิ่งขึ้นในที่สรรพสินค้าใหญ่..
อีวานแอ็ปเปิ้ล

ส่วนแทรกในขั้นตอนที่ 3 และ 5 ไม่ควรใช้กับตาราง new_master และให้ postgresql เลือกตารางลูก / พาร์ติชันที่เหมาะสมหรือไม่
pakman

@pakman ฟังก์ชั่นเพื่อกำหนดเด็กที่ถูกต้องจะไม่ได้รับการเพิ่มจนถึงขั้นตอนที่ 7
Evan Appleby

4

มีเครื่องมือใหม่ที่เรียกว่า pg_pathman ( https://github.com/postgrespro/pg_pathman ) ที่จะทำสิ่งนี้ให้คุณโดยอัตโนมัติ

ดังนั้นสิ่งต่อไปนี้จะทำ

SELECT create_range_partitions('master', 'dt_created', 
   '2015-01-01'::date, '1 day'::interval);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.