Cách chúng tôi sử dụng tính năng sao chép bị trì hoãn để khắc phục thảm họa bằng PostgreSQL

Cách chúng tôi sử dụng tính năng sao chép bị trì hoãn để khắc phục thảm họa bằng PostgreSQL
Bản sao không phải là bản sao lưu. Hay không? Đây là cách chúng tôi sử dụng bản sao trì hoãn để khôi phục sau khi vô tình xóa các phím tắt.

Chuyên gia cơ sở hạ tầng GitLab chịu trách nhiệm về công việc GitLab.com - phiên bản GitLab lớn nhất trong tự nhiên. Với 3 triệu người dùng và gần 7 triệu dự án, đây là một trong những trang SaaS nguồn mở lớn nhất có kiến ​​trúc chuyên dụng. Nếu không có hệ thống cơ sở dữ liệu PostgreSQL, cơ sở hạ tầng GitLab.com sẽ không thể tiến xa và chúng ta đang làm gì để đảm bảo khả năng chịu lỗi trong trường hợp xảy ra sự cố khi dữ liệu có thể bị mất. Thảm họa như vậy khó có thể xảy ra, nhưng chúng tôi đã chuẩn bị kỹ lưỡng và tích trữ nhiều cơ chế sao lưu và nhân rộng khác nhau.

Bản sao không phải là phương tiện sao lưu cơ sở dữ liệu (xem bên dưới). Nhưng bây giờ chúng ta sẽ xem cách khôi phục nhanh chóng dữ liệu vô tình bị xóa bằng cách sử dụng bản sao lười biếng: trên GitLab.com người sử dụng đã xóa phím tắt cho dự án gitlab-ce và mất kết nối với các yêu cầu và nhiệm vụ hợp nhất.

Với bản sao trả chậm, chúng tôi đã khôi phục dữ liệu chỉ trong 1,5 giờ. Hãy nhìn xem nó đã xảy ra như thế nào.

Phục hồi tại thời điểm với PostgreSQL

PostgreSQL có chức năng tích hợp sẵn để khôi phục trạng thái của cơ sở dữ liệu về một thời điểm cụ thể. Nó được gọi là Phục hồi tại thời điểm (PITR) và sử dụng các cơ chế tương tự để cập nhật bản sao: bắt đầu bằng ảnh chụp nhanh đáng tin cậy của toàn bộ cụm cơ sở dữ liệu (sao lưu cơ sở), chúng tôi áp dụng một loạt thay đổi trạng thái cho đến một thời điểm nhất định.

Để sử dụng tính năng này cho việc sao lưu nguội, chúng tôi thường xuyên tạo bản sao lưu cơ sở dữ liệu cơ bản và lưu trữ nó trong kho lưu trữ (kho lưu trữ GitLab nằm trong Bộ nhớ đám mây của Google). Chúng tôi cũng theo dõi những thay đổi về trạng thái của cơ sở dữ liệu bằng cách lưu trữ nhật ký ghi trước (nhật ký ghi trước, WAL). Và với tất cả những điều này đã sẵn sàng, chúng tôi có thể thực hiện PITR để khắc phục thảm họa: bắt đầu bằng ảnh chụp nhanh được chụp trước khi xảy ra lỗi và áp dụng các thay đổi từ kho lưu trữ WAL cho đến khi xảy ra lỗi.

Sao chép trì hoãn là gì?

Sao chép lười biếng là việc áp dụng các thay đổi từ WAL với độ trễ. Tức là giao dịch diễn ra trong một giờ X, nhưng nó sẽ xuất hiện trong bản sao với độ trễ d trong một tiếng nữa X + d.

PostgreSQL có 2 cách để thiết lập bản sao cơ sở dữ liệu vật lý: khôi phục bản sao lưu và sao chép trực tuyến. Khôi phục từ kho lưu trữ, về cơ bản hoạt động giống như PITR, nhưng liên tục: chúng tôi liên tục truy xuất các thay đổi từ kho lưu trữ WAL và áp dụng chúng vào bản sao. MỘT sao chép trực tuyến truy xuất trực tiếp luồng WAL từ máy chủ cơ sở dữ liệu ngược dòng. Chúng tôi thích khôi phục kho lưu trữ hơn - nó dễ quản lý hơn và có hiệu suất bình thường theo kịp cụm sản xuất.

Cách thiết lập khôi phục bị trì hoãn từ kho lưu trữ

Tùy chọn khôi phục được mô tả trong tập tin recovery.conf. Thí dụ:

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'

Với các tham số này, chúng tôi đã định cấu hình một bản sao bị trì hoãn với tính năng khôi phục bản sao lưu. Ở đây nó được sử dụng wal-e để trích xuất các phân đoạn WAL (restore_command) từ kho lưu trữ và các thay đổi sẽ được áp dụng sau tám giờ (recovery_min_apply_delay). Bản sao sẽ theo dõi các thay đổi về dòng thời gian trong kho lưu trữ, chẳng hạn như do chuyển đổi dự phòng cụm (recovery_target_timeline).

С recovery_min_apply_delay Bạn có thể thiết lập sao chép phát trực tuyến với độ trễ, nhưng có một số cạm bẫy ở đây liên quan đến các khe sao chép, phản hồi ở chế độ chờ nóng, v.v. Kho lưu trữ WAL cho phép bạn tránh chúng.

Thông số recovery_min_apply_delay chỉ xuất hiện trong PostgreSQL 9.3. Ở các phiên bản trước, để sao chép hoãn lại, bạn cần định cấu hình tổ hợp chức năng quản lý phục hồi (pg_xlog_replay_pause(), pg_xlog_replay_resume()) hoặc giữ các phân đoạn WAL trong kho lưu trữ trong suốt thời gian trì hoãn.

PostgreSQL làm điều này như thế nào?

Thật thú vị khi xem PostgreSQL triển khai phục hồi lười biếng như thế nào. Chúng ta hãy nhìn vào recoveryApplyDelay(XlogReaderState). Nó được gọi từ vòng lặp chính cho mỗi mục từ 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;
}

Điểm mấu chốt là độ trễ dựa trên thời gian vật lý được ghi trong dấu thời gian cam kết giao dịch (xtime). Như bạn có thể thấy, độ trễ chỉ áp dụng cho các cam kết và không ảnh hưởng đến các mục khác - tất cả các thay đổi đều được áp dụng trực tiếp và cam kết bị trì hoãn, vì vậy chúng ta sẽ chỉ thấy các thay đổi sau độ trễ được định cấu hình.

Cách sử dụng bản sao bị trì hoãn để khôi phục dữ liệu

Giả sử chúng ta có một cụm cơ sở dữ liệu và một bản sao bị chậm trễ XNUMX giờ trong quá trình sản xuất. Hãy xem cách khôi phục dữ liệu bằng một ví dụ vô tình xóa phím tắt.

Khi tìm hiểu vấn đề, chúng tôi quá trình khôi phục kho lưu trữ đã bị tạm dừng đối với một bản sao bị trì hoãn:

SELECT pg_xlog_replay_pause();

Khi tạm dừng, chúng tôi không gặp rủi ro rằng bản sao sẽ lặp lại yêu cầu DELETE. Một điều hữu ích nếu bạn cần thời gian để tìm hiểu mọi thứ.

Vấn đề là bản sao bị trì hoãn phải đến thời điểm trước khi có yêu cầu DELETE. Chúng tôi đã biết gần đúng thời gian thực tế của việc loại bỏ. Chúng tôi đã xóa recovery_min_apply_delay và thêm recovery_target_time в recovery.conf. Đây là cách bản sao đến đúng thời điểm mà không bị chậm trễ:

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

Với dấu thời gian, tốt hơn hết bạn nên giảm bớt phần thừa để không bỏ sót. Đúng, mức giảm càng lớn thì chúng ta càng mất nhiều dữ liệu. Một lần nữa, nếu chúng ta bỏ lỡ yêu cầu DELETE, mọi thứ sẽ bị xóa một lần nữa và bạn sẽ phải bắt đầu lại (hoặc thậm chí sao lưu nguội cho PITR).

Chúng tôi đã khởi động lại phiên bản Postgres bị trì hoãn và các phân đoạn WAL được lặp lại cho đến thời gian được chỉ định. Bạn có thể theo dõi tiến trình ở giai đoạn này bằng cách hỏi:

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;

Nếu dấu thời gian không còn thay đổi thì quá trình khôi phục đã hoàn tất. Hành động có thể được tùy chỉnh recovery_target_actionđể đóng, tăng cấp hoặc tạm dừng phiên bản sau khi thử lại (theo mặc định, phiên bản này bị tạm dừng).

Cơ sở dữ liệu đã trở lại trạng thái trước yêu cầu đáng tiếc đó. Ví dụ: bây giờ bạn có thể xuất dữ liệu. Chúng tôi đã xuất dữ liệu nhãn đã xóa và tất cả các liên kết đến các vấn đề cũng như các yêu cầu hợp nhất rồi chuyển chúng vào cơ sở dữ liệu sản xuất. Nếu tổn thất quy mô lớn, bạn chỉ cần quảng bá bản sao và sử dụng nó làm bản chính. Nhưng sau đó tất cả những thay đổi sau thời điểm mà chúng ta đã phục hồi sẽ bị mất.

Thay vì sử dụng dấu thời gian, tốt hơn nên sử dụng ID giao dịch. Sẽ rất hữu ích khi ghi lại các ID này, ví dụ: cho các câu lệnh DDL (chẳng hạn như DROP TABLE), bằng cách sử dụng log_statements = 'ddl'. Nếu chúng tôi có ID giao dịch, chúng tôi sẽ lấy recovery_target_xid và chạy mọi thứ đến giao dịch trước khi có yêu cầu DELETE.

Quay lại làm việc rất đơn giản: xóa tất cả thay đổi khỏi recovery.conf và khởi động lại Postgres. Bản sao sẽ sớm bị trễ tám giờ và chúng tôi đã chuẩn bị cho những rắc rối trong tương lai.

Quyền lợi phục hồi

Với bản sao trả chậm thay vì bản sao lưu nguội, bạn không phải mất hàng giờ để khôi phục toàn bộ hình ảnh từ kho lưu trữ. Ví dụ: chúng tôi phải mất 2 giờ để có được toàn bộ bản sao lưu cơ bản XNUMX TB. Và sau đó bạn vẫn phải áp dụng toàn bộ WAL hàng ngày để khôi phục về trạng thái mong muốn (trong trường hợp xấu nhất).

Bản sao hoãn lại tốt hơn bản sao lưu nguội theo hai cách:

  1. Không cần phải xóa toàn bộ bản sao lưu cơ bản khỏi kho lưu trữ.
  2. Có một khoảng thời gian tám giờ cố định cho các phân đoạn WAL phải được lặp lại.

Chúng tôi cũng liên tục kiểm tra xem liệu có thể tạo PITR từ WAL hay không và chúng tôi sẽ nhanh chóng nhận thấy lỗi hỏng hoặc các vấn đề khác với kho lưu trữ WAL bằng cách theo dõi độ trễ của bản sao bị trì hoãn.

Trong ví dụ này, chúng tôi mất 50 phút để khôi phục, nghĩa là tốc độ là 110 GB dữ liệu WAL mỗi giờ (bản lưu trữ vẫn bật AWS S3). Tổng cộng, chúng tôi đã giải quyết được sự cố và khôi phục dữ liệu trong 1,5 giờ.

Kết quả: nơi mà bản sao hoãn lại hữu ích (và nơi nào không)

Sử dụng bản sao bị trì hoãn làm cách sơ cứu nếu bạn vô tình làm mất dữ liệu và nhận thấy sự cố này trong độ trễ đã định cấu hình.

Nhưng hãy nhớ: bản sao không phải là bản sao lưu.

Sao lưu và sao chép có các mục đích khác nhau. Một bản sao lưu lạnh sẽ có ích nếu bạn vô tình thực hiện DELETE hoặc DROP TABLE. Chúng tôi tạo bản sao lưu từ kho lưu trữ lạnh và khôi phục trạng thái trước đó của bảng hoặc toàn bộ cơ sở dữ liệu. Nhưng đồng thời yêu cầu DROP TABLE gần như được sao chép ngay lập tức trong tất cả các bản sao trên cụm làm việc, vì vậy việc sao chép thông thường sẽ không giúp ích được gì ở đây. Bản thân bản sao giữ cho cơ sở dữ liệu luôn sẵn sàng khi các máy chủ riêng lẻ được thuê và phân phối tải.

Ngay cả với bản sao trả chậm, đôi khi chúng tôi thực sự cần một bản sao lưu lạnh ở một nơi an toàn nếu xảy ra lỗi trung tâm dữ liệu, hư hỏng tiềm ẩn hoặc các sự kiện khác không thể nhận thấy ngay lập tức. Bản sao một mình là không sử dụng ở đây.

Ghi. Trên GitLab.com Hiện tại, chúng tôi chỉ bảo vệ chống mất dữ liệu ở cấp hệ thống và không khôi phục dữ liệu ở cấp độ người dùng.

Nguồn: www.habr.com

Thêm một lời nhận xét