چگونه از Lazy Replication برای Disaster Recovery با PostgreSQL استفاده کردیم

چگونه از Lazy Replication برای Disaster Recovery با PostgreSQL استفاده کردیم
Replication یک نسخه پشتیبان نیست. یا نه؟ در اینجا نحوه استفاده از Lazy Replication برای بازیابی با حذف تصادفی میانبرها آمده است.

متخصصان زیرساخت GitLab مسئول کار است GitLab.com - بزرگترین نمونه GitLab در طبیعت. با 3 میلیون کاربر و تقریباً 7 میلیون پروژه، یکی از بزرگترین سایت های منبع باز SaaS با معماری اختصاصی است. بدون سیستم پایگاه داده PostgreSQL، زیرساخت GitLab.com دور از دسترس نخواهد بود، و کاری که ما برای تحمل خطا در صورت بروز هر گونه خرابی انجام نمی دهیم، زمانی که ممکن است داده ها را از دست بدهید. بعید است که چنین فاجعه ای رخ دهد، اما ما به خوبی آماده شدیم و با مکانیسم های مختلف پشتیبان و تکرار تهیه کردیم.

Replication ابزار پشتیبان گیری از پایگاه داده شما نیست (زیر را ببینید). اما اکنون خواهیم دید که چگونه به سرعت داده های حذف شده به طور تصادفی را با استفاده از تکرار تنبل بازیابی کنیم: on GitLab.com کاربر میانبر حذف شد برای پروژه gitlab-ce و اتصالات با درخواست ها و وظایف ادغام از دست رفت.

با کپی تاخیری، ما اطلاعات را تنها در 1,5 ساعت بازیابی کردیم. ببین چطور بود

بازیابی به موقع با PostgreSQL

PostgreSQL دارای یک تابع داخلی است که وضعیت پایگاه داده را به یک نقطه زمانی خاص بازیابی می کند. نامیده می شود بازیابی نقطه در زمان (PITR) و از مکانیزم‌های مشابهی استفاده می‌کند که یک ماکت را به‌روز نگه می‌دارد: با شروع یک عکس فوری مطمئن از کل کلاستر پایگاه داده (پشتیبان گیری پایه)، یک سری تغییرات حالت را تا یک نقطه زمانی خاص اعمال می‌کنیم.

برای استفاده از این ویژگی برای پشتیبان گیری سرد، ما به طور منظم یک نسخه پشتیبان از پایگاه داده پایه تهیه می کنیم و آن را در یک آرشیو ذخیره می کنیم (بایگانی های GitLab در ذخیره سازی ابری گوگل). ما همچنین تغییرات وضعیت پایگاه داده را با بایگانی کردن یک گزارش پیش از نوشتن (ثبت پیش ثبت نام، وال). و با همه اینها، ما می توانیم 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'

با این تنظیمات، ما یک replica تاخیری را با بازیابی بایگانی پیکربندی کرده ایم. در اینجا استفاده شده است وال ای برای استخراج بخش های WAL (restore_command) از آرشیو، و تغییرات پس از هشت ساعت اعمال می شود (recovery_min_apply_delay). Replica تغییرات جدول زمانی را در بایگانی مشاهده می‌کند، مثلاً به دلیل خرابی خوشه (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). همانطور که می بینید، تاخیر فقط برای commit ها اعمال می شود و روی رکوردهای دیگر تأثیر نمی گذارد - همه تغییرات به طور مستقیم اعمال می شوند و commit با تاخیر انجام می شود، بنابراین ما تغییرات را تنها پس از تاخیر پیکربندی شده مشاهده خواهیم کرد.

نحوه استفاده از ماکت تنبل برای بازیابی اطلاعات

فرض کنید یک خوشه پایگاه داده در حال تولید و یک ماکت با تاخیر هشت ساعته داریم. بیایید نحوه بازیابی اطلاعات را با استفاده از یک مثال ببینیم حذف تصادفی میانبرها.

وقتی متوجه مشکل شدیم، ما بازیابی نسخه پشتیبان متوقف شد برای کپی تاخیری:

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برای بستن، ترویج یا توقف نمونه پس از تلاش مجدد (به طور پیش فرض مکث می شود).

پایگاه داده قبل از آن درخواست بد به این ایالت آمد. اکنون می توانید، برای مثال، داده ها را صادر کنید. ما داده‌های برچسب راه دور و همه پیوندهای مربوط به مسائل و درخواست‌های ادغام را صادر کرده‌ایم و آنها را به پایگاه داده تولید منتقل کرده‌ایم. اگر تلفات زیاد باشد، می توانید به سادگی ماکت را تبلیغ کنید و از آن به عنوان اصلی استفاده کنید. اما پس از لحظه ای که ما بهبود یافتیم، همه تغییرات از بین خواهند رفت.

بهتر است از شناسه تراکنش ها به جای مهر زمانی استفاده کنید. ثبت این شناسه ها، به عنوان مثال، برای دستورات DDL (مانند DROP TABLE)، با استفاده از log_statements = 'ddl'. اگر شناسه تراکنش داشتیم می گرفتیم recovery_target_xid و همه چیز را قبل از درخواست به تراکنش انجام داد DELETE.

بازگشت به کار بسیار ساده است: همه تغییرات را حذف کنید recovery.conf و postgres را مجددا راه اندازی کنید. به زودی نشانه دوباره هشت ساعت تاخیر خواهد داشت و ما برای مشکلات آینده آماده هستیم.

مزایای بازیابی

با یک کپی تأخیر، به جای یک نسخه پشتیبان سرد، مجبور نیستید ساعت ها را صرف بازیابی کل عکس فوری از بایگانی کنید. به عنوان مثال، ما به پنج ساعت زمان نیاز داریم تا کل بک آپ پایه 2 ترابایتی را دریافت کنیم. و پس از آن شما هنوز هم باید کل WAL روزانه را اعمال کنید تا به حالت دلخواه بازگردید (در بدترین حالت).

یک کپی با تاخیر از دو طریق بهتر از یک نسخه پشتیبان سرد است:

  1. شما نیازی به دریافت کل نسخه پشتیبان از بایگانی ندارید.
  2. یک پنجره هشت ساعته ثابت از بخش های WAL وجود دارد که باید تکرار شود.

همچنین، ما دائماً در حال بررسی هستیم که ببینیم آیا WAL می‌تواند PITR شود یا خیر، و با نظارت بر بک‌لوگ کپی تأخیرشده، به سرعت متوجه خرابی یا سایر مشکلات بایگانی WAL می‌شویم.

در این مثال، 50 دقیقه طول کشید تا بازیابی کنیم، یعنی سرعت 110 گیگابایت داده WAL در ساعت بود (بایگانی هنوز روشن بود AWS S3). در کل مشکل را حل کردیم و داده ها را در 1,5 ساعت بازیابی کردیم.

خلاصه: جاهایی که کپی تاخیری مفید است (و جایی که نه)

اگر به طور تصادفی داده ها را از دست دادید و متوجه این فاجعه در تاخیر پیکربندی شده شدید، از تکرار تاخیری به عنوان کمک اولیه استفاده کنید.

اما به خاطر داشته باشید: تکرار یک نسخه پشتیبان نیست.

پشتیبان گیری و تکرار اهداف متفاوتی دارند. اگر به طور تصادفی تهیه کنید، یک نسخه پشتیبان سرد مفید خواهد بود DELETE یا DROP TABLE. ما یک نسخه پشتیبان از ذخیره سازی سرد تهیه می کنیم و وضعیت قبلی یک جدول یا کل پایگاه داده را بازیابی می کنیم. اما در عین حال درخواست DROP TABLE تقریباً فوراً در همه کپی‌ها در خوشه کاری تکثیر می‌شود، بنابراین تکرار منظم در اینجا ذخیره نمی‌شود. Replication خود پایگاه داده را زمانی که سرورهای جداگانه اجاره می‌کنند در دسترس نگه می‌دارد و بار را توزیع می‌کند.

حتی با یک کپی با تأخیر، گاهی اوقات واقعاً به یک نسخه پشتیبان سرد در مکانی امن نیاز داریم، اگر به طور ناگهانی خرابی مرکز داده، آسیب پنهان یا رویدادهای دیگری رخ دهد که بلافاصله متوجه آن نمی‌شوید. در اینجا از یک تکرار هیچ معنایی وجود ندارد.

یادداشت. بر GitLab.com ما در حال حاضر فقط در برابر از دست دادن داده ها در سطح سیستم محافظت می کنیم و داده ها را در سطح کاربر بازیابی نمی کنیم.

منبع: www.habr.com

اضافه کردن نظر