เราใช้ Lazy Replication สำหรับ Disaster Recovery ด้วย PostgreSQL อย่างไร

เราใช้ Lazy Replication สำหรับ Disaster Recovery ด้วย PostgreSQL อย่างไร
การจำลองแบบไม่ใช่การสำรองข้อมูล หรือไม่? ต่อไปนี้คือวิธีที่เราใช้การจำลองแบบแบบขี้เกียจเพื่อการกู้คืนโดยการลบทางลัดโดยไม่ตั้งใจ

ผู้เชี่ยวชาญด้านโครงสร้างพื้นฐาน GitLab รับผิดชอบงาน GitLab.คอม - ตัวอย่าง GitLab ที่ใหญ่ที่สุดในธรรมชาติ ด้วยจำนวนผู้ใช้ 3 ล้านคนและเกือบ 7 ล้านโครงการ จึงเป็นหนึ่งในไซต์ SaaS แบบโอเพ่นซอร์สที่ใหญ่ที่สุดที่มีสถาปัตยกรรมเฉพาะ หากปราศจากระบบฐานข้อมูล PostgreSQL โครงสร้างพื้นฐานของ GitLab.com ก็จะไปได้ไม่ไกล และสิ่งที่เราไม่ได้ทำเพื่อการยอมรับข้อบกพร่องในกรณีที่เกิดความล้มเหลวใดๆ ก็ตาม ซึ่งอาจทำให้ข้อมูลสูญหายได้ ไม่น่าเป็นไปได้ที่ความหายนะดังกล่าวจะเกิดขึ้น แต่เราได้เตรียมการมาอย่างดีและสำรองข้อมูลและกลไกการจำลองแบบต่างๆ

การจำลองแบบไม่ใช่เครื่องมือสำรองฐานข้อมูลของคุณ (ดูด้านล่าง). แต่ตอนนี้เราจะดูวิธีกู้คืนข้อมูลที่ถูกลบโดยไม่ตั้งใจอย่างรวดเร็วโดยใช้การจำลองแบบขี้เกียจ: เปิด GitLab.คอม ผู้ใช้งาน ทางลัดที่ลบออก สำหรับโครงการ gitlab-ce และสูญเสียการเชื่อมต่อกับคำขอผสานและงาน

ด้วยแบบจำลองที่ล่าช้า เราจึงกู้คืนข้อมูลได้ในเวลาเพียง 1,5 ชั่วโมง ดูว่ามันเป็นอย่างไร

การกู้คืนตามเวลาด้วย PostgreSQL

PostgreSQL มีฟังก์ชันในตัวที่จะกู้คืนสถานะของฐานข้อมูลไปยังเวลาที่กำหนด มันถูกเรียกว่า การกู้คืนตามเวลา (PITR) และใช้กลไกเดียวกันที่ทำให้แบบจำลองทันสมัย: เริ่มต้นด้วยสแน็ปช็อตที่เชื่อถือได้ของคลัสเตอร์ฐานข้อมูลทั้งหมด (การสำรองข้อมูลฐาน) เราใช้ชุดของการเปลี่ยนแปลงสถานะจนถึงเวลาที่กำหนด

ในการใช้คุณสมบัตินี้สำหรับการสำรองข้อมูลแบบ Cold เราทำการสำรองข้อมูลฐานข้อมูลพื้นฐานเป็นประจำและจัดเก็บไว้ในไฟล์เก็บถาวร (ไฟล์เก็บถาวร GitLab อยู่ใน ที่เก็บข้อมูลบนคลาวด์ของ Google). นอกจากนี้ เรายังตรวจสอบการเปลี่ยนแปลงสถานะของฐานข้อมูลโดยเก็บถาวรบันทึกการเขียนล่วงหน้า (เขียนบันทึกล่วงหน้า, วอล). และทั้งหมดนี้ เราสามารถทำ PITR สำหรับการกู้คืนระบบได้: เราเริ่มต้นด้วยสแน็ปช็อตที่ถ่ายก่อนเกิดข้อผิดพลาด และนำการเปลี่ยนแปลงจากไฟล์เก็บถาวร WAL ไปใช้กับข้อขัดข้อง

การจำลองแบบล่าช้าคืออะไร?

การจำลองแบบล่าช้าเป็นการนำการเปลี่ยนแปลงจาก WAL มาใช้โดยมีความล่าช้า นั่นคือการทำธุรกรรมเกิดขึ้นในชั่วโมง Xแต่จะปรากฏในแบบจำลองด้วยความล่าช้า d ในหนึ่งชั่วโมง X + d.

มี 2 ​​วิธีในการตั้งค่าแบบจำลองฐานข้อมูลจริงใน PostgreSQL: การกู้คืนไฟล์เก็บถาวรและการจำลองแบบสตรีมมิง กำลังกู้คืนจากไฟล์เก็บถาวรโดยหลักแล้วจะทำงานเหมือนกับ PITR แต่อย่างต่อเนื่อง: เราแยกการเปลี่ยนแปลงจากไฟล์เก็บถาวร WAL อย่างต่อเนื่องและนำไปใช้กับแบบจำลอง ก การจำลองแบบสตรีมมิ่ง ดึงสตรีม WAL โดยตรงจากโฮสต์ฐานข้อมูลอัปสตรีม เราต้องการคืนค่าจากไฟล์เก็บถาวร - จัดการได้ง่ายกว่าและมีประสิทธิภาพปกติซึ่งไม่ล้าหลังคลัสเตอร์การผลิต

วิธีตั้งค่าการกู้คืนข้อมูลสำรองที่ล่าช้า

ตัวเลือกการกู้คืน อธิบายไว้ในไฟล์ recovery.conf. ตัวอย่างเช่น:

standby_mode = 'on'
restore_command = '/usr/bin/envdir /etc/wal-e.d/env /opt/wal-e/bin/wal-e wal-fetch -p 4 "%f" "%p"'
recovery_min_apply_delay = '8h'
recovery_target_timeline = 'latest'

ด้วยการตั้งค่าเหล่านี้ เราได้กำหนดค่าแบบจำลองที่ล่าช้าพร้อมการคืนค่าไฟล์เก็บถาวร ใช้ที่นี่ วอล-อี เพื่อแยกส่วน WAL (restore_command) จากไฟล์เก็บถาวร และการเปลี่ยนแปลงจะมีผลหลังจากแปดชั่วโมง (recovery_min_apply_delay). แบบจำลองจะเฝ้าดูการเปลี่ยนแปลงไทม์ไลน์ในไฟล์เก็บถาวร เช่น เนื่องจากคลัสเตอร์ล้มเหลว (recovery_target_timeline).

С recovery_min_apply_delay คุณสามารถตั้งค่าการจำลองการสตรีมแบบแฝงได้ แต่มีข้อผิดพลาดสองสามประการที่เกี่ยวข้องกับสล็อตการจำลองแบบ ข้อเสนอแนะสำรองด่วน และอื่นๆ ไฟล์เก็บถาวร WAL หลีกเลี่ยงพวกเขา

พารามิเตอร์ recovery_min_apply_delay ปรากฏเฉพาะใน PostgreSQL 9.3 ในเวอร์ชันก่อนหน้า การจำลองแบบล่าช้าต้องใช้การรวมกัน ฟังก์ชั่นการจัดการการกู้คืน (pg_xlog_replay_pause(), pg_xlog_replay_resume()) หรือเก็บส่วน WAL ในไฟล์เก็บถาวรในช่วงระยะเวลาของการหน่วงเวลา

PostgreSQL ทำอย่างไร

เป็นเรื่องที่น่าสนใจที่จะดูว่า PostgreSQL ใช้การคืนค่าแบบขี้เกียจได้อย่างไร ลองดูที่ recoveryApplyDelay(XlogReaderState). เรียกว่ามาจาก วนซ้ำหลัก สำหรับแต่ละรายการจาก WAL

static bool
recoveryApplyDelay(XLogReaderState *record)
{
    uint8       xact_info;
    TimestampTz xtime;
    long        secs;
    int         microsecs;

    /* nothing to do if no delay configured */
    if (recovery_min_apply_delay <= 0)
        return false;

    /* no delay is applied on a database not yet consistent */
    if (!reachedConsistency)
        return false;

    /*
     * Is it a COMMIT record?
     *
     * We deliberately choose not to delay aborts since they have no effect on
     * MVCC. We already allow replay of records that don't have a timestamp,
     * so there is already opportunity for issues caused by early conflicts on
     * standbys.
     */
    if (XLogRecGetRmid(record) != RM_XACT_ID)
        return false;

    xact_info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;

    if (xact_info != XLOG_XACT_COMMIT &&
        xact_info != XLOG_XACT_COMMIT_PREPARED)
        return false;

    if (!getRecordTimestamp(record, &xtime))
        return false;

    recoveryDelayUntilTime =
        TimestampTzPlusMilliseconds(xtime, recovery_min_apply_delay);

    /*
     * Exit without arming the latch if it's already past time to apply this
     * record
     */
    TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
                        &secs, &microsecs);
    if (secs <= 0 && microsecs <= 0)
        return false;

    while (true)
    {
        // Shortened:
        // Use WaitLatch until we reached recoveryDelayUntilTime
        // and then
        break;
    }
    return true;
}

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

วิธีการใช้ Lazy Replica ในการกู้คืนข้อมูล

สมมติว่าเรามีคลัสเตอร์ฐานข้อมูลในการผลิตและแบบจำลองที่มีความล่าช้าแปดชั่วโมง ลองดูวิธีการกู้คืนข้อมูลโดยใช้ตัวอย่าง การลบทางลัดโดยไม่ตั้งใจ.

เมื่อเราทราบปัญหาแล้ว หยุดการกู้คืนข้อมูลสำรองชั่วคราว สำหรับแบบจำลองที่ล่าช้า:

SELECT pg_xlog_replay_pause();

เมื่อหยุดชั่วคราว เราไม่มีความเสี่ยงที่ตัวจำลองจะทำซ้ำคำขอ DELETE. สิ่งที่มีประโยชน์ถ้าคุณต้องการเวลาในการคิดทุกอย่างออก

บรรทัดล่างคือแบบจำลองที่ล่าช้าจะต้องไปถึงช่วงเวลาก่อนคำขอ DELETE. เรารู้เวลาทางกายภาพของการกำจัดโดยประมาณ เราลบออก recovery_min_apply_delay และเพิ่ม recovery_target_time в recovery.conf. ดังนั้นแบบจำลองจึงมาถึงช่วงเวลาที่เหมาะสมโดยไม่ชักช้า:

recovery_target_time = '2018-10-12 09:25:00+00'

ด้วยการประทับเวลา จะเป็นการดีกว่าที่จะลดส่วนเกินเพื่อไม่ให้พลาด จริงอยู่ที่ยิ่งลดลงมากเท่าใด ข้อมูลก็ยิ่งสูญเสียมากขึ้นเท่านั้น อีกครั้งหากเราข้ามคำขอ DELETEทุกอย่างจะถูกลบอีกครั้งและคุณจะต้องเริ่มต้นใหม่ (หรือแม้แต่ทำการสำรองข้อมูลแบบเย็นสำหรับ PITR)

เรารีสตาร์ทอินสแตนซ์ Postgres ที่ล่าช้า และส่วน WAL ถูกทำซ้ำจนกว่าจะถึงเวลาที่กำหนด คุณสามารถติดตามความคืบหน้าในขั้นตอนนี้ได้โดยการสอบถาม:

SELECT
  -- current location in WAL
  pg_last_xlog_replay_location(),
  -- current transaction timestamp (state of the replica)
  pg_last_xact_replay_timestamp(),
  -- current physical time
  now(),
  -- the amount of time still to be applied until recovery_target_time has been reached
  '2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay;

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

ฐานข้อมูลมาถึงสถานะก่อนคำขอที่โชคร้าย ตอนนี้คุณสามารถส่งออกข้อมูลได้ เราได้ส่งออกข้อมูลฉลากระยะไกลและลิงก์ทั้งหมดไปยังปัญหาและรวมคำขอและโอนไปยังฐานข้อมูลการผลิต หากการสูญเสียมีมาก คุณสามารถโปรโมตแบบจำลองและใช้เป็นตัวหลักได้ แต่การเปลี่ยนแปลงทั้งหมดจะหายไปหลังจากช่วงเวลาที่เรากู้คืน

ควรใช้รหัสธุรกรรมแทนการประทับเวลา การบันทึก ID เหล่านี้มีประโยชน์ เช่น สำหรับคำสั่ง DDL (เช่น DROP TABLE), โดยใช้ log_statements = 'ddl'. ถ้าเรามี ID ธุรกรรม เราจะใช้ recovery_target_xid และดำเนินการทุกอย่างลงไปที่การทำธุรกรรมก่อนการร้องขอ DELETE.

การกลับไปทำงานนั้นง่ายมาก: ลบการเปลี่ยนแปลงทั้งหมดจาก recovery.conf และรีสตาร์ท postgres อีกไม่นาน สัญญาณจะดีเลย์แปดชั่วโมงอีกครั้ง และเราพร้อมสำหรับปัญหาในอนาคต

ผลประโยชน์การกู้คืน

ด้วยแบบจำลองที่ล่าช้า แทนที่จะเป็นการสำรองข้อมูลแบบเย็น คุณไม่จำเป็นต้องใช้เวลาหลายชั่วโมงในการกู้คืนสแน็ปช็อตทั้งหมดจากไฟล์เก็บถาวร ตัวอย่างเช่น เราต้องใช้เวลาห้าชั่วโมงในการสำรองข้อมูลพื้นฐานขนาด 2 TB ทั้งหมด จากนั้นคุณยังคงต้องใช้ WAL รายวันทั้งหมดเพื่อกู้คืนไปยังสถานะที่ต้องการ (ในกรณีที่เลวร้ายที่สุด)

แบบจำลองที่ล่าช้านั้นดีกว่าการสำรองข้อมูลแบบเย็นในสองวิธี:

  1. คุณไม่จำเป็นต้องรับข้อมูลสำรองฐานทั้งหมดจากไฟล์เก็บถาวร
  2. มีช่วงเวลาแปดชั่วโมงคงที่สำหรับส่วน WAL ที่ต้องทำซ้ำ

นอกจากนี้ เรากำลังตรวจสอบอย่างต่อเนื่องเพื่อดูว่า WAL สามารถเป็น PITRed ได้หรือไม่ และเราจะสังเกตเห็นความเสียหายหรือปัญหาอื่นๆ อย่างรวดเร็วกับไฟล์เก็บถาวร WAL โดยการตรวจสอบงานในมือของแบบจำลองที่ล่าช้า

ในตัวอย่างนี้ เราใช้เวลา 50 นาทีในการกู้คืน กล่าวคือ ความเร็วคือข้อมูล WAL 110 GB ต่อชั่วโมง (ไฟล์เก็บถาวรยังเปิดอยู่ AWS S3). โดยรวมแล้วเราแก้ไขปัญหาและกู้คืนข้อมูลได้ใน 1,5 ชั่วโมง

สรุป: ที่การจำลองแบบล่าช้ามีประโยชน์ (และที่ใดไม่)

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

แต่โปรดจำไว้ว่า: การจำลองแบบไม่ใช่การสำรองข้อมูล

การสำรองข้อมูลและการทำซ้ำมีวัตถุประสงค์ที่แตกต่างกัน การสำรองข้อมูลแบบเย็นจะมีประโยชน์หากคุณทำโดยไม่ตั้งใจ DELETE หรือ DROP TABLE. เราทำการสำรองข้อมูลจากห้องเย็นและกู้คืนสถานะก่อนหน้าของตารางหรือฐานข้อมูลทั้งหมด แต่ในเวลาเดียวกันคำขอ DROP TABLE ทำซ้ำเกือบจะทันทีในแบบจำลองทั้งหมดบนคลัสเตอร์การทำงาน ดังนั้นการจำลองแบบปกติจะไม่บันทึกที่นี่ การจำลองตัวเองทำให้ฐานข้อมูลพร้อมใช้งานเมื่อเซิร์ฟเวอร์แต่ละเครื่องถูกเช่าและกระจายโหลด

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

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

ที่มา: will.com

เพิ่มความคิดเห็น