PostgreSQL ile Felaket Kurtarma için Lazy Replication'ı Nasıl Kullandık?

PostgreSQL ile Felaket Kurtarma için Lazy Replication'ı Nasıl Kullandık?
Çoğaltma bir yedekleme değildir. Ya da değil? Kısayolları yanlışlıkla silerek kurtarma için tembel çoğaltmayı şu şekilde kullandık.

altyapı uzmanları Çalışmalardan GitLab sorumludur. GitLab.com - doğadaki en büyük GitLab örneği. 3 milyon kullanıcı ve yaklaşık 7 milyon proje ile özel mimariye sahip en büyük açık kaynaklı SaaS sitelerinden biridir. PostgreSQL veritabanı sistemi olmadan, GitLab.com altyapısı fazla ileri gidemez ve herhangi bir arıza durumunda veri kaybedebileceğiniz hata toleransı için yapmadığımız şey. Böyle bir felaketin gerçekleşmesi pek olası değil, ancak iyi hazırlandık ve çeşitli yedekleme ve çoğaltma mekanizmalarını stokladık.

Çoğaltma, veritabanı yedekleme aracınız değildir (aşağıya bakın). Ancak şimdi, tembel çoğaltmayı kullanarak yanlışlıkla silinen verileri nasıl hızlı bir şekilde kurtaracağımızı göreceğiz: açık GitLab.com kullanıcı kısayol kaldırıldı proje için gitlab-ce ve birleştirme istekleri ve görevleriyle bağlantıları kaybetti.

Gecikmeli çoğaltma ile verileri yalnızca 1,5 saatte kurtardık. Nasıl olduğunu görün.

PostgreSQL ile zamanında kurtarma

PostgreSQL, bir veritabanının durumunu zamanın belirli bir noktasına geri yükleyen yerleşik bir işleve sahiptir. denir Belirli Bir Noktada Kurtarma (PITR) ve bir kopyayı güncel tutan aynı mekanizmaları kullanır: tüm veritabanı kümesinin güvenilir bir anlık görüntüsüyle (temel yedekleme) başlayarak, belirli bir zamana kadar bir dizi durum değişikliği uygularız.

Soğuk yedekleme için bu özelliği kullanmak için, düzenli olarak bir temel veritabanı yedeği yaparız ve bunu bir arşivde saklarız (GitLab arşivleri, Google bulut depolama). Ayrıca, önceden yazma günlüğünü arşivleyerek veritabanı durumu değişikliklerini de izleriz (ileriye dönük günlüğe yaz, WAL). Ve tüm bunlarla, felaket kurtarma için PITR yapabiliriz: hatadan önce alınan bir anlık görüntü ile başlar ve WAL arşivindeki değişiklikleri çökmeye kadar uygularız.

Gecikmeli replikasyon nedir?

Gecikmeli replikasyon, WAL'deki değişikliklerin gecikmeli olarak uygulanmasıdır. Yani, işlem saatte gerçekleşti X, ancak yinelemede gecikmeli olarak görünecektir d saat başı X + d.

PostgreSQL'de fiziksel bir veritabanı kopyası kurmanın 2 yolu vardır: arşiv geri yükleme ve akış çoğaltma. Bir arşivden geri yükleme, temelde PITR gibi çalışır, ancak sürekli olarak: WAL arşivinden değişiklikleri sürekli olarak çıkarıyor ve onları kopyaya uyguluyoruz. A akış çoğaltma WAL akışını doğrudan yukarı akış veritabanı ana bilgisayarından getirir. Bir arşivden geri yüklemeyi tercih ediyoruz - yönetimi daha kolaydır ve üretim kümesinin gerisinde kalmayan normal performansa sahiptir.

Gecikmeli yedekleme kurtarma nasıl kurulur?

Kurtarma seçenekleri dosyada açıklanan recovery.conf. Örnek:

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'

Bu ayarlarla, arşiv geri yüklemeli gecikmeli bir kopya yapılandırdık. Burada kullanılan vel-e WAL segmentlerini çıkarmak için (restore_command) arşivden alınır ve değişiklikler sekiz saat sonra uygulanır (recovery_min_apply_delay). Çoğaltma, arşivdeki bir küme yük devretmesi gibi zaman çizelgesi değişikliklerini izleyecektir (recovery_target_timeline).

С recovery_min_apply_delay Gecikmeli akış çoğaltmasını ayarlayabilirsiniz, ancak çoğaltma yuvaları, etkin yedek geri bildirimi vb. ile ilişkili birkaç tuzak vardır. WAL arşivi bunlardan kaçınır.

Parametre recovery_min_apply_delay yalnızca PostgreSQL 9.3'te göründü. Önceki sürümlerde, gecikmeli çoğaltma, aşağıdakilerin bir kombinasyonunu gerektirir: kurtarma yönetimi işlevleri (pg_xlog_replay_pause(), pg_xlog_replay_resume()) veya arşivdeki WAL segmentlerini gecikme süresi boyunca tutun.

PostgreSQL bunu nasıl yapıyor?

PostgreSQL'in tembel geri yüklemeyi nasıl uyguladığını görmek ilginç. Şuna bakalım recoveryApplyDelay(XlogReaderState). den denir ana döngü tekrarı WAL'dan gelen her giriş için.

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;
}

Sonuç olarak, gecikme, işlem taahhüdünün zaman damgasında kaydedilen fiziksel süreye bağlıdır (xtime). Gördüğünüz gibi, gecikme yalnızca taahhütler için geçerlidir ve diğer kayıtları etkilemez - tüm değişiklikler doğrudan uygulanır ve taahhüt ertelenir, bu nedenle değişiklikleri yalnızca yapılandırılan gecikmeden sonra göreceğiz.

Veri kurtarma için tembel çoğaltma nasıl kullanılır?

Diyelim ki üretimde bir veritabanı kümemiz ve sekiz saatlik gecikmeli bir kopyamız var. Bir örnek kullanarak verilerin nasıl kurtarılacağını görelim kısayolların yanlışlıkla silinmesi.

Sorunun farkına vardığımızda, duraklatılmış yedekleme kurtarma gecikmeli çoğaltma için:

SELECT pg_xlog_replay_pause();

Bir duraklamayla, kopyanın isteği tekrarlama riski yoktu. DELETE. Her şeyi anlamak için zamana ihtiyacınız varsa kullanışlı bir şey.

Sonuç olarak, ertelenen kopya istekten önceki ana ulaşmalıdır. DELETE. Çıkarmanın fiziksel zamanını yaklaşık olarak biliyorduk. Taşındı recovery_min_apply_delay ve eklendi recovery_target_time в recovery.conf. Böylece kopya gecikmeden doğru ana ulaşır:

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

Zaman damgalarıyla, kaçırmamak için fazlalığı azaltmak daha iyidir. Doğru, düşüş ne kadar büyük olursa, o kadar çok veri kaybederiz. Yine isteği atlarsak DELETE, her şey yeniden silinecek ve baştan başlamanız (hatta PITR için soğuk bir yedek almanız) gerekecek.

Geciken Postgres örneğini yeniden başlattık ve belirtilen zamana kadar WAL segmentleri tekrarlandı. Bu aşamadaki ilerlemeyi aşağıdakileri sorgulayarak takip edebilirsiniz:

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;

Zaman damgası artık değişmiyorsa geri yükleme tamamlanmıştır. İşlemi özelleştirebilirsiniz recovery_target_actionyeniden denemeden sonra örneği kapatmak, ilerletmek veya duraklatmak için (varsayılan olarak duraklar).

Veri tabanı, bu talihsiz istekten önce devlete geldi. Artık örneğin verileri dışa aktarabilirsiniz. Uzak etiket verilerini ve sorunlara ve birleştirme isteklerine yönelik tüm bağlantıları dışa aktardık ve bunları üretim veritabanına aktardık. Kayıplar büyükse, kopyayı tanıtabilir ve ana kopya olarak kullanabilirsiniz. Ancak, iyileştiğimiz andan sonra tüm değişiklikler kaybolacaktır.

Zaman damgaları yerine işlem kimliklerini kullanmak daha iyidir. Bu kimlikleri, örneğin DDL ifadeleri için (örneğin, DROP TABLE), kullanarak log_statements = 'ddl'. Bir işlem kimliğimiz olsaydı, alırdık recovery_target_xid ve istekten önce işleme kadar her şeyi yaptı DELETE.

İşe geri dönmek çok basit: tüm değişiklikleri kaldırın. recovery.conf ve postgres'i yeniden başlatın. Yakında kopyada yine sekiz saatlik bir gecikme olacak ve gelecekteki sorunlara hazırız.

İyileşme Faydaları

Soğuk bir yedekleme yerine gecikmeli bir çoğaltmayla, anlık görüntünün tamamını arşivden geri yüklemek için saatler harcamanıza gerek kalmaz. Örneğin, 2 TB temel yedeğin tamamını almak için beş saate ihtiyacımız var. Ve sonra, istenen duruma (en kötü durumda) geri dönmek için günlük WAL'ın tamamını uygulamanız gerekir.

Gecikmeli bir çoğaltma, soğuk bir yedeklemeden iki şekilde daha iyidir:

  1. Tüm temel yedeği arşivden almanıza gerek yoktur.
  2. Tekrarlanması gereken sekiz saatlik sabit bir WAL segment penceresi vardır.

Ayrıca, WAL'ın PITRed olup olmadığını sürekli olarak kontrol ediyoruz ve gecikmeli kopyanın birikmiş iş listesini izleyerek WAL arşivindeki bozulmaları veya diğer sorunları hemen fark edebiliyoruz.

Bu örnekte, geri yüklememiz 50 dakika sürdü, yani hız saatte 110 GB WAL verisiydi (arşiv hala açıktı AWS S3). Toplamda sorunu çözdük ve verileri 1,5 saatte geri yükledik.

Özet: gecikmeli bir çoğaltmanın yararlı olduğu (ve olmadığı) durumlarda

Yanlışlıkla veri kaybederseniz ve yapılandırılan gecikme içinde bu felaketi fark ederseniz, gecikmeli çoğaltmayı ilk yardım olarak kullanın.

Ancak unutmayın: çoğaltma bir yedekleme değildir.

Yedekleme ve replikasyonun farklı amaçları vardır. Yanlışlıkla yaptıysanız, soğuk bir yedekleme kullanışlı olacaktır. DELETE veya DROP TABLE. Soğuk depodan bir yedekleme yapıyoruz ve bir tablonun veya tüm veritabanının önceki durumunu geri yüklüyoruz. Ama aynı zamanda istek DROP TABLE çalışma kümesindeki tüm kopyalarda neredeyse anında yeniden üretilir, bu nedenle normal çoğaltma burada kaydedilmez. Çoğaltma, tek tek sunucular kiralandığında veritabanını kullanılabilir durumda tutar ve yükü dağıtır.

Gecikmeli bir çoğaltmada bile, aniden bir veri merkezi arızası, gizli hasar veya hemen fark etmediğiniz başka olaylar olursa, bazen gerçekten güvenli bir yerde soğuk bir yedeklemeye ihtiyacımız olur. Burada bir çoğaltmadan hiçbir anlam yok.

Dikkat. Üzerinde GitLab.com şu anda yalnızca sistem düzeyinde veri kaybına karşı koruma sağlıyoruz ve verileri kullanıcı düzeyinde geri yüklemiyoruz.

Kaynak: habr.com

Yorum ekle