بکاپ خودکار روزانه از MySQL با cron و حذف بکاپ‌های قدیمی

بکاپ دستی همیشه فراموش می‌شود. در این راهنما یک اسکریپت امن و ساده می‌نویسیم که هر شب از دیتابیس بکاپ فشرده می‌گیرد، بکاپ‌های قدیمی‌تر از چند روز را حذف می‌کند و نتیجه را در لاگ می‌نویسد.

دیتابیس و بکاپمتوسط12 دقیقه

۱ساخت پوشهٔ بکاپ و فایل اتصال

پوشهٔ بکاپ را جدا از پوشهٔ دیتابیس بسازید و فقط برای root قابل خواندن کنید. اتصال را با ~/.my.cnf انجام دهید تا رمز در اسکریپت نباشد.

bash
sudo mkdir -p /var/backups/mysql
sudo chmod 700 /var/backups/mysql

sudo tee /root/.my.cnf > /dev/null <<'EOF'
[client]
user=backup
password=YOUR_STRONG_PASSWORD
EOF
sudo chmod 600 /root/.my.cnf

۲نوشتن اسکریپت بکاپ

اسکریپت زیر ابتدا در فایل موقت می‌نویسد و فقط در صورت موفقیت آن را نهایی می‌کند؛ پس اگر mysqldump وسط کار خطا بدهد فایل ناقص به‌عنوان بکاپ باقی نمی‌ماند.

bash
sudo tee /usr/local/bin/mysql-backup.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

DB_NAME="appdb"
BACKUP_DIR="/var/backups/mysql"
KEEP_DAYS=14
STAMP=$(date +%F_%H-%M)
OUT="$BACKUP_DIR/${DB_NAME}_${STAMP}.sql.gz"

trap 'rm -f "$OUT.tmp"' ERR

mysqldump --single-transaction --quick --routines --triggers --events \
  --no-tablespaces "$DB_NAME" | gzip > "$OUT.tmp"

gzip -t "$OUT.tmp"
mv "$OUT.tmp" "$OUT"

# حذف بکاپ‌های قدیمی‌تر از KEEP_DAYS روز
find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +"$KEEP_DAYS" -delete

echo "$(date '+%F %T') OK  $OUT ($(du -h "$OUT" | cut -f1))"
EOF
sudo chmod 700 /usr/local/bin/mysql-backup.sh

برای چند دیتابیس، DB_NAME را حلقه کنید یا به‌جای آن از --all-databases استفاده کنید.

۳اجرای دستی برای تست

قبل از زمان‌بندی، یک بار اسکریپت را دستی اجرا کنید و خروجی و فایل ساخته‌شده را ببینید.

bash
sudo /usr/local/bin/mysql-backup.sh
ls -lh /var/backups/mysql/

۴زمان‌بندی با cron

با crontab -e (برای root) خط زیر را اضافه کنید تا هر شب ساعت ۳ اجرا شود و خروجی در لاگ ذخیره شود. ساعت کم‌ترافیک سایت را انتخاب کنید.

bash
sudo crontab -e

# هر شب ساعت 03:00
0 3 * * * /usr/local/bin/mysql-backup.sh >> /var/log/mysql-backup.log 2>&1

بعد از اولین اجرای خودکار حتماً لاگ را ببینید: tail /var/log/mysql-backup.log

۵چرخش لاگ خود بکاپ

با logrotate جلوی بزرگ شدن فایل لاگ را بگیرید.

bash
sudo tee /etc/logrotate.d/mysql-backup > /dev/null <<'EOF'
/var/log/mysql-backup.log {
    weekly
    rotate 8
    compress
    missingok
    notifempty
}
EOF

۶کپی بکاپ روی سرور دیگر

بکاپی که فقط روی همان سرور باشد با خراب شدن سرور از بین می‌رود. بعد از بکاپ، آن را با rsync به یک سرور یا فضای ذخیره‌سازی دیگر بفرستید. این خط را به انتهای اسکریپت اضافه کنید.

bash
rsync -az --delete -e "ssh -i /root/.ssh/backup_key" \
  /var/backups/mysql/ backup@203.0.113.20:/srv/backups/mysql/

قانون ۳-۲-۱: حداقل سه نسخه، روی دو نوع رسانهٔ متفاوت و یک نسخه بیرون از محل اصلی.

۷بررسی دوره‌ای ریستور

ماهی یک بار یکی از بکاپ‌ها را روی یک دیتابیس آزمایشی ریستور کنید و تعداد رکوردهای جدول‌های اصلی را مقایسه کنید. تنها همین کار ثابت می‌کند بکاپ‌ها واقعاً قابل استفاده‌اند.

bash
mysql -e "CREATE DATABASE restore_test"
zcat /var/backups/mysql/appdb_2026-09-29_03-00.sql.gz | mysql restore_test
mysql -e "SELECT COUNT(*) FROM restore_test.users"
mysql -e "DROP DATABASE restore_test"

پرسش‌های پرتکرار دربارهٔ بکاپ خودکار روزانه از MySQL با cron و حذف بکاپ‌های قدیمی

بکاپ‌ها را چند روز نگه دارم؟

معمولاً ۷ تا ۳۰ روز روزانه، به‌علاوهٔ یک بکاپ ماهانه برای مدت طولانی‌تر. به حجم دیسک و میزان تغییر داده بستگی دارد؛ تعداد روزها را با KEEP_DAYS تنظیم کنید.

چرا cron بکاپ نگرفت؟

معمولاً به‌خاطر مسیر ناقص دستورها (در cron از /usr/local/bin/ و مسیر کامل استفاده کنید)، دسترسی نداشتن فایل اجرایی (chmod +x) یا نبودن ~/.my.cnf برای کاربر اجراکنندهٔ cron است. لاگ را با grep CRON /var/log/syslog ببینید.