برای مدیران پایگاه داده و مهندسان DevOps، سناریوهای کمی به اندازه یک رویداد فاجعهبار دادهای استرسزا هستند. چه یک دستور DROP TABLE تصادفی باشد، چه یک مهاجرت (Migration) ناموفق پایگاه داده که میلیونها ردیف را خراب کرده باشد، و چه یک حمله باجافزاری مخرب، پشتیبانگیریهای شبانه استاندارد اغلب کافی نیستند. اگر فاجعهای در ساعت ۴:۰۰ بعدازظهر رخ دهد و آخرین نسخه پشتیبان شما مربوط به ساعت ۲:۰۰ بامداد باشد، با ۱۴ ساعت از دست رفتن دائمی دادهها مواجه هستید.
اینجاست که بازیابی در لحظه (Point-in-Time Recovery یا به اختصار PITR) در PostgreSQL بسیار ارزشمند میشود. PITR به شما امکان میدهد پایگاه داده خود را به یک میکروثانیه قبل از وقوع رویداد فاجعهبار بازگردانید و هدف نقطه بازیابی (RPO) خود را به نزدیک صفر برسانید.
در این راهنمای جامع، ما معماری PITR در PostgreSQL را بررسی میکنیم، مراحل دقیق پیادهسازی برای محیطهای مدرن PostgreSQL (نسخه ۱۲ و جدیدتر) را مرور کرده و بهترین شیوههای تولید را برای اطمینان از تابآوری دادههای شما مورد بحث قرار میدهیم.
درک معماری PITR در PostgreSQL
برای اجرای موفقیتآمیز یک PITR، باید مکانیسمهای زیربنایی نحوه مدیریت پایداری و وضعیت دادهها توسط PostgreSQL را درک کنید. PITR به دو مؤلفه اساسی متکی است: پشتیبانهای پایه (Base Backups) و لاگهای پیشنویس (Write-Ahead Logs یا WAL).
لاگهای پیشنویس (WAL)
PostgreSQL با استفاده از Write-Ahead Logging یکپارچگی دادهها را تضمین میکند. قبل از اینکه هر تغییری (درج، بهروزرسانی، حذف) در فایلهای داده واقعی پایگاه داده نوشته شود، ابتدا در WAL ثبت میشود.
بهطور پیشفرض، فایلهای WAL به بخشهای ۱۶ مگابایتی تقسیم میشوند. در صورت بروز خرابی، PostgreSQL این لاگها را دوباره پخش (Replay) میکند تا پایگاه داده را به یک وضعیت سازگار بازگرداند. برای PITR، ما این کار را از طریق آرشیو کردن WAL یک گام فراتر میبریم. بهجای اینکه اجازه دهیم PostgreSQL بخشهای قدیمی WAL را بازیافت کند، پایگاه داده را پیکربندی میکنیم تا هر فایل WAL تکمیلشده را در یک مکان ذخیرهسازی ثانویه و امن کپی (آرشیو) کند.
پشتیبانهای پایه (Base Backups)
پشتیبان پایه یک کپی فیزیکی و در سطح فایلسیستم از دایرکتوری داده PostgreSQL (PGDATA) شماست. این پشتیبان به عنوان نقطه شروع برای بازیابی عمل میکند. از آنجایی که پایگاه داده در حین تهیه پشتیبان پایه فعال است و بهطور مداوم تغییر میکند، خودِ پشتیبان از نظر فنی ناسازگار است.
فرآیند بازیابی
PITR فرآیند ترکیب این دو مؤلفه است. شما پشتیبان پایه ناسازگار را بازیابی میکنید و سپس به PostgreSQL دستور میدهید که فایلهای WAL آرشیو شده را بهصورت متوالی روی آن پشتیبان پخش کند. از آنجایی که WAL حاوی یک رکورد دقیق و زمانی از هر تراکنش است، PostgreSQL میتواند تاریخچه پایگاه داده را پخش کرده و در زمان دقیق، شماره توالی لاگ (LSN) یا شناسه تراکنشی که شما مشخص میکنید، متوقف شود.
پیشنیازها: پیکربندی PostgreSQL برای آرشیو کردن WAL
قبل از اینکه بتوانید PITR را انجام دهید، پایگاه داده شما باید برای آرشیو کردن فایلهای WAL پیکربندی شده باشد. این کار مستلزم تغییر فایل postgresql.conf شماست.
نکته: تغییر wal_level مستلزم راهاندازی مجدد سرویس PostgreSQL است.
# postgresql.conf
# سطح WAL را روی replica (یا logical) تنظیم کنید که حاوی دادههای کافی برای PITR است
wal_level = replica
# فعالسازی آرشیو کردن WAL
archive_mode = on
# دستور کپی کردن فایل WAL به فضای ذخیرهسازی آرشیو را تعریف کنید
# %p مسیر فایل WAL و %f نام فایل است
archive_command = 'test ! -f /mnt/wal_archive/%f && cp %p /mnt/wal_archive/%f'
مهم: دستور archive_command ارائهشده در بالا یک مثال پایه با استفاده از cp است. دستور test ! -f تضمین میکند که ما بهطور تصادفی یک فایل آرشیو موجود را بازنویسی نکنیم. در یک محیط تولید واقعی، تکیه بر دستورات ساده شل میتواند شکننده باشد. مونتهای شبکه ممکن است قطع شوند و دیسکها پر شوند.
مرحله ۱: گرفتن پشتیبان پایه
با فعال بودن آرشیو کردن WAL، گام بعدی گرفتن یک پشتیبان پایه است. ما برای این کار از ابزار بومی pg_basebackup استفاده میکنیم.
دستور زیر را به عنوان کاربر postgres اجرا کنید:
pg_basebackup -h localhost -U postgres -D /mnt/backups/base_backup_$(date +%Y%m%d) -Ft -z -Xs -P
تشریح دستور:
* -D: دایرکتوری مقصد برای پشتیبان.
* -Ft: فرمت را روی tar تنظیم میکند (یک فایل base.tar.gz ایجاد میکند).
* -z: فشردهسازی gzip را فعال میکند.
* -Xs: فایلهای WAL تولیدشده در حین فرآیند پشتیبانگیری را استریم کرده و در پشتیبان قرار میدهد. این کار تضمین میکند که پشتیبان پایه بلافاصله پس از استخراج، سازگار شود.
* -P: پیشرفت کار را نمایش میدهد.
مرحله ۲: اجرای بازیابی در لحظه (PG 12+)
نکته: PostgreSQL 12 تغییرات قابلتوجهی در فرآیند بازیابی ایجاد کرد و فایل سنتی recovery.conf را منسوخ نمود. مراحل زیر برای PostgreSQL 12، 13، 14، 15، 16 و نسخههای بعد از آن اعمال میشود.
فرض کنید یک توسعهدهنده بهطور تصادفی دستور DELETE FROM users; را دقیقاً در ساعت 2023-11-15 14:35:00 UTC اجرا کرده است. ما باید پایگاه داده را به ساعت 14:34:59 بازگردانیم.
۱. متوقف کردن سرویس PostgreSQL
ابتدا، پایگاه داده را متوقف کنید تا از هرگونه اتصال یا تغییر داده بیشتر جلوگیری شود.
sudo systemctl stop postgresql
۲. آمادهسازی دایرکتوری داده
شما باید دایرکتوری داده فعلی و خرابشده را پاک کنید. آرشیوهای WAL یا پشتیبانهای پایه خود را حذف نکنید.
# تغییر نام دایرکتوری خرابشده به عنوان یک اقدام احتیاطی
mv /var/lib/postgresql/14/main /var/lib/postgresql/14/main_corrupted
# ایجاد یک دایرکتوری داده جدید و خالی
mkdir /var/lib/postgresql/14/main
chmod 700 /var/lib/postgresql/14/main
chown postgres:postgres /var/lib/postgresql/14/main
۳. بازیابی پشتیبان پایه
آخرین پشتیبان پایه خود را در دایرکتوری داده تازه ایجاد شده استخراج کنید.
tar -xzvf /mnt/backups/base_backup_20231114/base.tar.gz -C /var/lib/postgresql/14/main/
۴. پیکربندی تنظیمات بازیابی
برای اینکه به PostgreSQL بگویید وارد حالت بازیابی شود، باید یک فایل خالی به نام recovery.signal در ریشه دایرکتوری داده ایجاد کنید.
touch /var/lib/postgresql/14/main/recovery.signal
chown postgres:postgres /var/lib/postgresql/14/main/recovery.signal
سپس، پارامترهای بازیابی را پیکربندی کنید. در PostgreSQL مدرن، این تنظیمات مستقیماً در postgresql.conf (یا postgresql.auto.conf) قرار میگیرند.
# postgresql.conf (تنظیمات بازیابی)
# دستور برای بازیابی فایلهای WAL آرشیو شده
restore_command = 'cp /mnt/wal_archive/%f %p'
# زمان دقیق برای توقف بازیابی
recovery_target_time = '2023-11-15 14:34:59 UTC'
# اقدام پس از رسیدن به هدف (promote باعث میشود پایگاه داده نوشتن را بپذیرد)
recovery_target_action = 'promote'
۵. شروع بازیابی
سرویس PostgreSQL را شروع کنید. پایگاه داده فایل recovery.signal را شناسایی کرده، restore_command را میخواند و شروع به دریافت و پخش فایلهای WAL میکند.
sudo systemctl start postgresql
لاگهای PostgreSQL را بهدقت زیر نظر بگیرید. باید خروجی مشابه زیر را ببینید:
LOG: starting point-in-time recovery to 2023-11-15 14:34:59+00
LOG: restored log file "000000010000000A000000F1" from archive
LOG: redo starts at A/F1000028
LOG: recovery stopping before commit of transaction 45892, time 2023-11-15 14:35:00.123456+00
LOG: recovery has paused
LOG: promoted to timeline 2
پس از ارتقا (Promote)، PostgreSQL نام recovery.signal را به recovery.signal.done تغییر میدهد و پایگاه داده اکنون فعال است و اتصالات خواندن/نوشتن را در یک خط زمانی جدید میپذیرد.
اهداف بازیابی پیشرفته
اگرچه بازیابی مبتنی بر زمان (recovery_target_time) رایجترین روش است، PostgreSQL از اهداف جایگزین بسیار دقیقی پشتیبانی میکند:
recovery_target_name: میتوانید قبل از عملیاتهای پرخطر، با استفاده ازSELECT pg_create_restore_point('pre_migration');نقاط بازیابی نامگذاریشده در منطق برنامه خود ایجاد کنید. سپس میتوانید مستقیماً به این نام بازیابی کنید.recovery_target_lsn: بازیابی تا یک شماره توالی لاگ (Log Sequence Number) خاص. این کار زمانی مفید است که در حال تجزیه فایلهای WAL باpg_waldumpهستید و آفست بایت دقیق یک تراکنش مخرب را شناسایی میکنید.recovery_target_xid: بازیابی تا یک شناسه تراکنش (Transaction ID) خاص.
بهترین شیوههای تولید برای PITR در PostgreSQL
پیادهسازی PITR تنها نیمی از راه است؛ حفظ یک وضعیت بازیابی از فاجعه قابلاعتماد نیازمند هوشیاری مداوم است.
نظارت بر موفقیت دستور آرشیو
اگر archive_command شما شکست بخورد (مثلاً دیسک آرشیو پر باشد)، PostgreSQL به انباشتن فایلهای WAL در دایرکتوری pg_wal ادامه میدهد تا زمانی که دیسک اصلی پر شود و منجر به خرابی پایگاه داده گردد. همیشه نمای pg_stat_archiver را نظارت کنید:
SELECT last_failed_wal, last_failed_time, stats_reset
FROM pg_stat_archiver
WHERE failed_count > 0;
اگر صف آرشیو شروع به رشد کرد، در پشته نظارتی خود (Prometheus، Datadog و غیره) هشدار تنظیم کنید.
اتوماسیون و تست منظم بازیابیها
یک پشتیبان تا زمانی که با موفقیت بازیابی نشود، فقط یک نظریه است. “پشتیبان شرودینگر” وضعیت خطرناکی برای هر مدیر پایگاه داده است. شما باید فرآیند راهاندازی یک سرور استیجینگ، دریافت آخرین پشتیبان پایه، پخش WALها و اجرای کوئریهای اعتبارسنجی دادهها را خودکار کنید.
استفاده از راهکارهای پشتیبانگیری سازمانی
اگرچه اسکریپتنویسی archive_command با cp یا rsync برای توسعه یا استقرار در مقیاس کوچک قابلقبول است، محیطهای تولید سازمانی نیازمند مدیریت چرخه حیات قوی، رمزنگاری، فشردهسازی و تکثیر خارج از سایت هستند.
پلتفرمهایی مانند CloudSave بهطور یکپارچه با PostgreSQL ادغام میشوند تا شکنندگی اسکریپتهای bash سفارشی را از بین ببرند. CloudSave زمانبندی پشتیبانهای پایه را خودکار کرده و آرشیوهای WAL را بهطور مداوم به فضای ذخیرهسازی ابری امن و غیرقابل تغییر استریم میکند. بهجای ویرایش دستی فایلهای پیکربندی و محاسبه زمانها در حین قطعیهای پرفشار، مدیران میتوانند از رابط یکپارچه CloudSave برای تعریف RPOهای دقیق و اجرای بازیابی در لحظه با یک کلیک استفاده کنند که بهطور چشمگیری زمان میانگین تا بازیابی (MTTR) را کاهش میدهد.
مدیریت ذخیرهسازی و نگهداری WAL
فایلهای WAL آرشیو شده اگر مدیریت نشوند، فضای دیسک را بهطور نامحدود اشغال خواهند کرد. شما باید یک سیاست نگهداری (Retention Policy) پیادهسازی کنید که با پشتیبانهای پایه شما همسو باشد. اگر پشتیبانهای پایه را برای ۳۰ روز نگه میدارید، فقط به ۳۰ روز آرشیو WAL نیاز دارید.
ابزارهایی مانند pg_archivecleanup میتوانند برای پاکسازی فایلهای WAL قدیمی استفاده شوند، اما این نیز حوزهای است که استفاده از یک پلتفرم پشتیبانگیری اختصاصی با منقضی کردن خودکار WALهایی که دیگر توسط هیچ پشتیبان پایه فعالی مورد نیاز نیستند، عملیات را ساده میکند.
نتیجهگیری
بازیابی در لحظه (PITR) در PostgreSQL یک ویژگی غیرقابلمذاکره برای پایگاههای داده حیاتی است. با درک تعامل بین پشتیبانهای پایه و لاگهای پیشنویس، و با پایبندی دقیق به پیکربندیهای مدرن و رویههای بازیابی، میتوانید سازمان خود را از دست دادن فاجعهبار دادهها محافظت کنید. به یاد داشته باشید که آرشیوهای خود را نظارت کنید، رویههای بازیابی خود را بهطور مکرر تست کنید و از ابزارهای سطح سازمانی استفاده کنید تا اطمینان حاصل کنید که استراتژی بازیابی از فاجعه شما به اندازه خودِ پایگاه داده تابآور است.