Bagaimana kami menggunakan replikasi tertunda untuk pemulihan bencana dengan PostgreSQL

Bagaimana kami menggunakan replikasi tertunda untuk pemulihan bencana dengan PostgreSQL
Replikasi bukan cadangan. Atau tidak? Inilah cara kami menggunakan replikasi yang ditangguhkan untuk memulihkan pintasan yang terhapus secara tidak sengaja.

Spesialis infrastruktur GitLab bertanggung jawab atas pekerjaan tersebut GitLab.com - contoh GitLab terbesar di alam. Dengan 3 juta pengguna dan hampir 7 juta proyek, ini adalah salah satu situs SaaS open source terbesar dengan arsitektur khusus. Tanpa sistem database PostgreSQL, infrastruktur GitLab.com tidak akan berfungsi jauh, dan apa yang kami lakukan untuk memastikan toleransi kesalahan jika terjadi kegagalan ketika data bisa hilang. Kecil kemungkinan bencana seperti itu akan terjadi, namun kami telah mempersiapkan diri dengan baik dan memiliki berbagai mekanisme pencadangan dan replikasi.

Replikasi bukan merupakan sarana untuk membuat cadangan database (lihat di bawah). Namun sekarang kita akan melihat cara cepat memulihkan data yang terhapus secara tidak sengaja menggunakan replikasi malas: aktif GitLab.com pemakai menghapus pintasannya untuk proyeknya gitlab-ce dan kehilangan koneksi dengan permintaan dan tugas penggabungan.

Dengan replika yang ditangguhkan, kami memulihkan data hanya dalam 1,5 jam. Lihat bagaimana hal itu terjadi.

Pemulihan titik waktu dengan PostgreSQL

PostgreSQL memiliki fungsi bawaan yang memulihkan keadaan database ke titik waktu tertentu. Itu disebut Pemulihan Point-in-Time (PITR) dan menggunakan mekanisme yang sama untuk menjaga replika tetap mutakhir: dimulai dengan snapshot yang andal dari seluruh cluster database (cadangan dasar), kami menerapkan serangkaian perubahan status hingga titik waktu tertentu.

Untuk menggunakan fitur ini untuk pencadangan dingin, kami secara rutin membuat pencadangan basis data dasar dan menyimpannya dalam arsip (arsip GitLab ada di Penyimpanan awan Google). Kami juga memantau perubahan status database dengan mengarsipkan log write-ahead (log tulis-depan, WAL). Dan dengan semua ini, kita dapat melakukan PITR untuk pemulihan bencana: dimulai dengan snapshot yang diambil sebelum kegagalan, dan menerapkan perubahan dari arsip WAL hingga kegagalan.

Apa itu replikasi yang ditangguhkan?

Replikasi malas adalah penerapan perubahan dari WAL dengan penundaan. Artinya, transaksi terjadi dalam satu jam X, namun akan muncul di replika dengan penundaan d dalam jam X + d.

PostgreSQL memiliki 2 cara untuk menyiapkan replika database fisik: pemulihan cadangan dan replikasi streaming. Memulihkan dari arsip, pada dasarnya berfungsi seperti PITR, tetapi terus menerus: kami terus-menerus mengambil perubahan dari arsip WAL dan menerapkannya ke replika. A replikasi streaming langsung mengambil aliran WAL dari host database hulu. Kami lebih memilih pemulihan arsip - lebih mudah dikelola dan memiliki kinerja normal yang dapat mengimbangi kluster produksi.

Cara mengatur pemulihan tertunda dari arsip

Opsi pemulihan dijelaskan dalam file 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 parameter ini, kami mengonfigurasi replika yang ditangguhkan dengan pemulihan cadangan. Di sini digunakan wa-e untuk mengekstrak segmen WAL (restore_command) dari arsip, dan perubahan akan diterapkan setelah delapan jam (recovery_min_apply_delay). Replika akan mengamati perubahan garis waktu dalam arsip, misalnya karena kegagalan klaster (recovery_target_timeline).

Π‘ recovery_min_apply_delay Anda dapat mengatur replikasi streaming dengan penundaan, namun ada beberapa kendala di sini yang terkait dengan slot replikasi, umpan balik siaga panas, dan sebagainya. Arsip WAL memungkinkan Anda menghindarinya.

Parameter recovery_min_apply_delay hanya muncul di PostgreSQL 9.3. Di versi sebelumnya, untuk replikasi yang ditangguhkan, Anda perlu mengonfigurasi kombinasinya fungsi manajemen pemulihan (pg_xlog_replay_pause(), pg_xlog_replay_resume()) atau simpan segmen WAL di arsip selama penundaan.

Bagaimana PostgreSQL melakukan ini?

Sangat menarik untuk melihat bagaimana PostgreSQL mengimplementasikan pemulihan yang lambat. Mari lihat recoveryApplyDelay(XlogReaderState). Itu disebut dari loop pengulangan utama untuk setiap entri 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 adalah penundaan didasarkan pada waktu fisik yang tercatat dalam stempel waktu penerapan transaksi (xtime). Seperti yang Anda lihat, penundaan hanya berlaku untuk penerapan dan tidak memengaruhi entri lain - semua perubahan diterapkan secara langsung, dan penerapan tertunda, jadi kami hanya akan melihat perubahan setelah penundaan yang dikonfigurasi.

Cara menggunakan replika tertunda untuk memulihkan data

Katakanlah kita memiliki cluster database dan replika dengan penundaan produksi delapan jam. Mari kita lihat cara memulihkan data menggunakan sebuah contoh secara tidak sengaja menghapus pintasan.

Ketika kami mengetahui masalahnya, kami pemulihan arsip telah dihentikan sementara untuk replika yang ditangguhkan:

SELECT pg_xlog_replay_pause();

Dengan jeda, kami tidak memiliki risiko replika tersebut akan mengulangi permintaan tersebut DELETE. Suatu hal yang berguna jika Anda perlu waktu untuk memikirkan semuanya.

Intinya replika yang ditangguhkan harus mencapai momen sebelum permintaan DELETE. Kami kira-kira mengetahui waktu fisik pemindahan. Kami telah menghapus recovery_min_apply_delay dan menambahkan recovery_target_time Π² recovery.conf. Beginilah cara replika mencapai momen yang tepat tanpa penundaan:

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

Dengan stempel waktu, lebih baik kurangi kelebihannya agar tidak ketinggalan. Benar, semakin besar penurunannya, semakin banyak data yang hilang. Sekali lagi, jika kita melewatkan permintaan tersebut DELETE, semuanya akan dihapus lagi dan Anda harus memulai dari awal (atau bahkan mengambil cadangan dingin untuk PITR).

Kami memulai kembali instance Postgres yang ditangguhkan dan segmen WAL diulang hingga waktu yang ditentukan. Anda dapat melacak kemajuan pada tahap ini dengan menanyakan:

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 stempel waktu tidak lagi berubah, pemulihan selesai. Tindakan dapat disesuaikan recovery_target_actionuntuk menutup, mempromosikan, atau menjeda instance setelah mencoba lagi (ini ditangguhkan secara default).

Basis data kembali ke keadaannya sebelum permintaan malang itu. Sekarang Anda dapat, misalnya, mengekspor data. Kami mengekspor data label yang dihapus dan semua tautan ke masalah serta permintaan penggabungan dan memindahkannya ke database produksi. Jika kerugiannya besar, Anda cukup mempromosikan replikanya dan menggunakannya sebagai replika utama. Tapi kemudian semua perubahan setelah kita pulih akan hilang.

Daripada stempel waktu, lebih baik menggunakan ID transaksi. Hal ini berguna untuk mencatat ID ini, misalnya, untuk pernyataan DDL (seperti DROP TABLE), dengan menggunakan log_statements = 'ddl'. Jika kami memiliki ID transaksi, kami akan mengambil recovery_target_xid dan menjalankan semuanya hingga transaksi sebelum permintaan DELETE.

Kembali bekerja sangat sederhana: hapus semua perubahan dari recovery.conf dan mulai ulang Postgres. Replika ini akan segera mengalami penundaan delapan jam lagi, dan kami siap menghadapi masalah di masa depan.

Manfaat Pemulihan

Dengan replika yang ditangguhkan dan bukan cadangan dingin, Anda tidak perlu menghabiskan waktu berjam-jam memulihkan seluruh gambar dari arsip. Misalnya, kami memerlukan waktu lima jam untuk mendapatkan seluruh cadangan dasar sebesar 2 TB. Dan kemudian Anda masih harus menerapkan seluruh WAL harian untuk pulih ke kondisi yang diinginkan (dalam kasus terburuk).

Replika yang ditangguhkan lebih baik daripada cadangan dingin dalam dua hal:

  1. Tidak perlu menghapus seluruh cadangan dasar dari arsip.
  2. Ada jangka waktu tetap delapan jam untuk segmen WAL yang harus diulang.

Kami juga terus-menerus memeriksa apakah mungkin membuat PITR dari WAL, dan kami akan segera mengetahui adanya kerusakan atau masalah lain pada arsip WAL dengan memantau kelambatan replika yang ditangguhkan.

Dalam contoh ini, kami membutuhkan waktu 50 menit untuk memulihkan, artinya kecepatan data WAL adalah 110 GB per jam (arsip masih aktif AWS S3). Secara total, kami memecahkan masalah dan memulihkan data dalam 1,5 jam.

Hasil: di mana replika yang ditangguhkan berguna (dan di mana tidak)

Gunakan replikasi tertunda sebagai pertolongan pertama jika Anda kehilangan data secara tidak sengaja dan melihat masalah ini dalam penundaan yang dikonfigurasi.

Namun perlu diingat: replikasi bukanlah cadangan.

Pencadangan dan replikasi memiliki tujuan berbeda. Cadangan dingin akan berguna jika Anda tidak sengaja membuatnya DELETE ΠΈΠ»ΠΈ DROP TABLE. Kami membuat cadangan dari cold storage dan memulihkan keadaan tabel atau seluruh database sebelumnya. Tapi sekaligus permintaan DROP TABLE hampir seketika direproduksi di semua replika pada cluster yang berfungsi, jadi replikasi biasa tidak akan membantu di sini. Replikasi itu sendiri membuat database tetap tersedia ketika masing-masing server disewakan dan mendistribusikan beban.

Bahkan dengan replika yang ditangguhkan, terkadang kita sangat membutuhkan cold backup di tempat yang aman jika terjadi kegagalan pusat data, kerusakan tersembunyi, atau kejadian lain yang tidak segera terlihat. Replikasi saja tidak ada gunanya di sini.

Catatan. Pada GitLab.com Saat ini kami hanya melindungi terhadap kehilangan data di tingkat sistem dan tidak memulihkan data di tingkat pengguna.

Sumber: www.habr.com

Tambah komentar