Cara Kami Menggunakan Lazy Replication untuk Pemulihan Bencana dengan PostgreSQL

Cara Kami Menggunakan Lazy Replication untuk Pemulihan Bencana dengan PostgreSQL
Replikasi bukan sandaran. Atau tidak? Begini cara kami menggunakan replikasi malas untuk pemulihan dengan memadamkan pintasan secara tidak sengaja.

pakar infrastruktur GitLab bertanggungjawab untuk kerja GitLab.com - contoh terbesar GitLab dalam alam semula jadi. Dengan 3 juta pengguna dan hampir 7 juta projek, ia merupakan salah satu tapak SaaS sumber terbuka terbesar dengan seni bina khusus. Tanpa sistem pangkalan data PostgreSQL, infrastruktur GitLab.com tidak akan pergi jauh, dan perkara yang kami tidak lakukan untuk toleransi kesalahan sekiranya berlaku sebarang kegagalan apabila anda boleh kehilangan data. Tidak mungkin malapetaka sebegini akan berlaku, tetapi kami membuat persediaan dengan baik dan menyediakan pelbagai mekanisme sandaran dan replikasi.

Replikasi bukan alat sandaran pangkalan data anda (lihat di bawah). Tetapi sekarang kita akan melihat bagaimana untuk memulihkan data yang dipadam secara tidak sengaja dengan cepat menggunakan replikasi malas: hidup GitLab.com pengguna pintasan yang dialih keluar untuk projek itu gitlab-ce dan kehilangan sambungan dengan permintaan dan tugasan gabungan.

Dengan replika tertunda, kami memulihkan data dalam masa 1,5 jam sahaja. Lihat bagaimana keadaannya.

Pemulihan titik dalam masa dengan PostgreSQL

PostgreSQL mempunyai fungsi terbina dalam yang memulihkan keadaan pangkalan data ke titik masa tertentu. Ia dikenali sebagai Pemulihan Point-in-Time (PITR) dan menggunakan mekanisme yang sama yang memastikan replika sentiasa dikemas kini: bermula dengan syot kilat yang boleh dipercayai bagi keseluruhan kluster pangkalan data (sandaran asas), kami menggunakan satu siri perubahan keadaan sehingga satu masa tertentu.

Untuk menggunakan ciri ini untuk sandaran sejuk, kami kerap membuat sandaran pangkalan data asas dan menyimpannya dalam arkib (arkib GitLab hidup dalam Storan awan Google). Kami juga memantau perubahan keadaan pangkalan data dengan mengarkibkan log tulis ke hadapan (tulis log di hadapan, WAL). Dan dengan semua ini, kita boleh melakukan PITR untuk pemulihan bencana: kita mulakan dengan syot kilat yang diambil sebelum ralat dan menggunakan perubahan daripada arkib WAL sehingga ranap sistem.

Apakah replikasi tertunda?

Replikasi tertunda ialah permohonan perubahan daripada WAL dengan kelewatan. Iaitu, transaksi berlaku pada jam tersebut X, tetapi ia akan muncul dalam replika dengan kelewatan d dalam masa sejam X + d.

Terdapat 2 cara untuk menyediakan replika pangkalan data fizikal dalam PostgreSQL: pemulihan arkib dan replikasi penstriman. Memulihkan daripada arkib, pada asasnya berfungsi seperti PITR, tetapi secara berterusan: kami sentiasa mengekstrak perubahan daripada arkib WAL dan menerapkannya pada replika. A replikasi penstriman mengambil strim WAL terus daripada hos pangkalan data huluan. Kami lebih suka memulihkan daripada arkib - ia lebih mudah diurus dan mempunyai prestasi biasa, yang tidak ketinggalan di belakang kelompok pengeluaran.

Bagaimana untuk menyediakan pemulihan sandaran tertunda

Pilihan Pemulihan diterangkan dalam fail recovery.conf. Contoh:

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'

Dengan tetapan ini, kami telah mengkonfigurasi replika tertunda dengan pemulihan arkib. Digunakan di sini wal-e untuk mengekstrak segmen WAL (restore_command) daripada arkib, dan perubahan akan digunakan selepas lapan jam (recovery_min_apply_delay). Replika akan melihat perubahan garis masa dalam arkib, seperti disebabkan kegagalan kelompok (recovery_target_timeline).

Π‘ recovery_min_apply_delay anda boleh menyediakan replikasi penstriman kependaman, tetapi terdapat beberapa masalah yang berkaitan dengan slot replikasi, maklum balas ganti panas dan sebagainya. Arkib WAL mengelakkan mereka.

Parameter recovery_min_apply_delay muncul hanya dalam PostgreSQL 9.3. Dalam versi sebelumnya, replikasi tertunda memerlukan gabungan fungsi pengurusan pemulihan (pg_xlog_replay_pause(), pg_xlog_replay_resume()) atau tahan segmen WAL dalam arkib untuk tempoh kelewatan.

Bagaimanakah PostgreSQL melakukannya?

Sangat menarik untuk melihat bagaimana PostgreSQL melaksanakan pemulihan malas. Jom tengok recoveryApplyDelay(XlogReaderState). Ia dipanggil dari ulangan gelung utama untuk setiap penyertaan dari 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;
}

Intinya ialah kelewatan adalah berdasarkan masa fizikal yang direkodkan dalam cap waktu komit transaksi (xtime). Seperti yang anda lihat, kelewatan hanya terpakai pada komit dan tidak menjejaskan rekod lain - semua perubahan digunakan secara langsung dan komit ditangguhkan, jadi kami akan melihat perubahan hanya selepas kelewatan yang dikonfigurasikan.

Cara menggunakan replika malas untuk pemulihan data

Katakan kita mempunyai kluster pangkalan data dalam pengeluaran dan replika dengan kelewatan lapan jam. Mari lihat cara memulihkan data menggunakan contoh pemadaman pintasan secara tidak sengaja.

Apabila kita menyedari masalah itu, kita pemulihan sandaran dijeda untuk replika tertunda:

SELECT pg_xlog_replay_pause();

Dengan jeda, kami tidak mempunyai risiko bahawa replika itu akan mengulangi permintaan itu DELETE. Perkara yang berguna jika anda memerlukan masa untuk memikirkan segala-galanya.

Intinya ialah replika yang ditangguhkan mesti mencapai masa sebelum permintaan DELETE. Kami kira-kira tahu masa fizikal penyingkiran. Kami mengeluarkan recovery_min_apply_delay dan ditambah recovery_target_time Π² recovery.conf. Jadi replika mencapai masa yang tepat tanpa berlengah-lengah:

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

Dengan cap masa, lebih baik kurangkan lebihan supaya tidak terlepas. Benar, semakin besar penurunan, semakin banyak data yang kita hilang. Sekali lagi, jika kita melangkau permintaan DELETE, semuanya akan dipadamkan sekali lagi dan anda perlu memulakan semula (atau mengambil sandaran sejuk untuk PITR).

Kami memulakan semula kejadian Postgres yang tertunda dan segmen WAL diulang sehingga masa yang ditentukan. Anda boleh menjejaki kemajuan pada peringkat ini dengan bertanya:

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;

Jika cap masa tidak lagi berubah, pemulihan selesai. Anda boleh menyesuaikan tindakan recovery_target_actionuntuk menutup, mempromosikan atau menjeda contoh selepas mencuba semula (ia dijeda secara lalai).

Pangkalan data datang ke negeri ini sebelum permintaan malang itu. Kini anda boleh, sebagai contoh, mengeksport data. Kami telah mengeksport data label jauh dan semua pautan kepada isu dan permintaan gabungan dan memindahkannya ke pangkalan data pengeluaran. Jika kerugian besar, anda boleh mempromosikan replika dan menggunakannya sebagai yang utama. Tetapi kemudian semua perubahan akan hilang selepas masa kami pulih.

Adalah lebih baik untuk menggunakan ID transaksi dan bukannya cap masa. Adalah berguna untuk merekodkan ID ini, contohnya, untuk pernyataan DDL (seperti DROP TABLE), dengan menggunakan log_statements = 'ddl'. Jika kami mempunyai ID transaksi, kami akan mengambil recovery_target_xid dan menjalankan segala-galanya ke urus niaga sebelum permintaan DELETE.

Kembali ke tempat kerja adalah sangat mudah: alih keluar semua perubahan daripada recovery.conf dan mulakan semula postgres. Tidak lama lagi isyarat akan mengalami kelewatan selama lapan jam, dan kami bersedia untuk menghadapi masalah pada masa hadapan.

Faedah Pemulihan

Dengan replika tertunda, bukannya sandaran sejuk, anda tidak perlu menghabiskan berjam-jam memulihkan keseluruhan gambar daripada arkib. Sebagai contoh, kita memerlukan lima jam untuk mendapatkan keseluruhan sandaran asas 2 TB. Dan kemudian anda masih perlu menggunakan keseluruhan WAL harian untuk pulih ke keadaan yang diingini (dalam kes yang paling teruk).

Replika tertunda adalah lebih baik daripada sandaran sejuk dalam dua cara:

  1. Anda tidak perlu mendapatkan keseluruhan sandaran asas daripada arkib.
  2. Terdapat tetingkap tetap lapan jam bagi segmen WAL yang mesti diulang.

Selain itu, kami sentiasa menyemak untuk melihat sama ada WAL boleh PITRed, dan kami akan segera melihat rasuah atau masalah lain dengan arkib WAL dengan memantau tunggakan replika yang tertangguh.

Dalam contoh ini, kami mengambil masa 50 minit untuk memulihkan, iaitu, kelajuan adalah 110 GB data WAL sejam (arkib masih dihidupkan AWS S3). Secara keseluruhan, kami menyelesaikan masalah dan memulihkan data dalam masa 1,5 jam.

Ringkasan: apabila replika tertunda berguna (dan jika tidak)

Gunakan replikasi tertunda sebagai pertolongan cemas jika anda kehilangan data secara tidak sengaja dan menyedari bencana ini dalam kelewatan yang dikonfigurasikan.

Tetapi perlu diingat: replikasi bukan sandaran.

Sandaran dan replikasi mempunyai tujuan yang berbeza. Sandaran sejuk akan berguna jika anda membuat secara tidak sengaja DELETE atau DROP TABLE. Kami membuat sandaran daripada storan sejuk dan memulihkan keadaan jadual sebelumnya atau keseluruhan pangkalan data. Tetapi pada masa yang sama permintaan DROP TABLE hampir serta-merta diterbitkan semula dalam semua replika pada kelompok kerja, jadi replikasi biasa tidak akan disimpan di sini. Replikasi itu sendiri memastikan pangkalan data tersedia apabila pelayan individu dipajak dan mengagihkan beban.

Walaupun dengan replika yang tertunda, kadangkala kami benar-benar memerlukan sandaran sejuk di tempat yang selamat, jika tiba-tiba terdapat kegagalan pusat data, kerosakan tersembunyi atau peristiwa lain yang anda tidak perasan dengan segera. Di sini dari satu replikasi tidak ada makna.

Nota. Pada GitLab.com pada masa ini kami hanya melindungi daripada kehilangan data pada peringkat sistem dan tidak memulihkan data pada peringkat pengguna.

Sumber: www.habr.com

Tambah komen