1. 程式人生 > >MySQL定時備份(全量備份+增量備份)

MySQL定時備份(全量備份+增量備份)

參考 zone7_實戰-MySQL定時備份系列文章

說明

產品上線後,資料非常非常重要,萬一哪天資料被誤刪,那麼就gg了,準備跑路吧。
所以要對線上的資料庫定時做全量備份增量備份

增量備份的優點是沒有重複資料,備份量不大,時間短。但缺點也很明顯,需要建立在上次完全備份及完全備份之後所有的增量才能恢復。

MySQL沒有提供直接的增量備份方法,但是可以通過mysql二進位制日誌間接實現增量備份。二進位制日誌對備份的意義如下:

  • 二進位制日誌儲存了所有更新或者可能更新資料的操作
  • 二進位制日誌在啟動MySQL伺服器後開始記錄,並在檔案達到所設大小或者收到flush logs 命令後重新建立新的日誌檔案
  • 只需定時執行flush logs 方法重新建立新的日誌,生成二進位制檔案序列,並及時把這些檔案儲存到一個安全的地方,即完成了一個時間段的增量備份。

全量備份

mysqldump --lock-all-tables --flush-logs --master-data=2 -u root -p test > backup_sunday_1_PM.sql
  • --lock-all-tables
    對於InnoDB將替換為 --single-transaction
  • --flush-logs
    結束當前日誌,生成並使用新日誌檔案
  • --master-data=2
    選項將會在輸出SQL中記錄下完全備份後新日誌檔案的名稱,用於日後恢復時參考,例如輸出的備份SQL檔案中含有:CHANGE MASTER TO MASTER_LOG_FILE='MySQL-bin.000002', MASTER_LOG_POS=106;
  • test
    該處的test表示資料庫test,如果想要將所有的資料庫備份,可以換成引數 --all-databases

全量備份指令碼shell

#!/bin/bash
# Program
# use mysqldump to Fully backup mysql data per week!
# History
# Path

# 使用者名稱、密碼、資料庫名
username="root"
password="tencns152"

Begin=`date +"%Y年%m月%d日 %H:%M:%S"`
# 備份目錄
BakDir=/home/mysql/backup
# 日誌檔案
LogFile=/home/mysql/backup/bak.log
# 備份檔案
Date=`date +%Y%m%d`
DumpFile="${dbName}_${Date}.sql"
GZDumpFile="${dbName}_${Date}.sql.tgz"

cd $BakDir
# 全量備份
/usr/local/mysql/bin/mysqldump -u${username} -p${password} --quick --events --all-databases --flush-logs --delete-master-logs --single-transaction > $DumpFile
# 打包
/bin/tar -zvcf $GZDumpFile $DumpFile
/bin/rm $DumpFile

Last=`date +"%Y年%m月%d日 %H:%M:%S"`
echo 開始:$Begin 結束:$Last $GZDumpFile succ >> $LogFile

# 刪除所有增量備份
cd $BakDir/daily
/bin/rm -f *

增量備份

1. 檢查log_bin是否開啟

進入mysql命令列,執行 show variables like '%log_bin%'

mysql> show variables like '%log_bin%';
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| log_bin                         | OFF   |
| log_bin_basename                |       |
| log_bin_index                   |       |
| log_bin_trust_function_creators | OFF   |
| log_bin_use_v1_row_events       | OFF   |
| sql_log_bin                     | ON    |
+---------------------------------+-------+
6 rows in set (0.01 sec)

如上所示,log_bin 未開啟;如果log_bin開啟,則跳過第2步,直接進入第3步。

2. 開啟 log_bin,並重啟mysql

  • 編輯 mysql 的配置檔案 vim /etc/my.cnf,在 mysqld 下面新增下面2條配置
[mysqld]
log-bin=/var/lib/mysql/mysql-bin
server_id=152

Tip1: 一定要加 server_id,否則會報錯。至於server_id的值,隨便設就可以。
Tip2: log_bin 中間可以下劃線_相連,也可以-減號相連。同理server_id也一樣。

  • 重啟mysql
service mysqld restart
  • 再次在mysql命令列中執行 show variables like '%log_bin%'
mysql> show variables like '%log_bin%';
+---------------------------------+--------------------------------+
| Variable_name                   | Value                          |
+---------------------------------+--------------------------------+
| log_bin                         | ON                             |
| log_bin_basename                | /var/lib/mysql/mysql-bin       |
| log_bin_index                   | /var/lib/mysql/mysql-bin.index |
| log_bin_trust_function_creators | OFF                            |
| log_bin_use_v1_row_events       | OFF                            |
| sql_log_bin                     | ON                             |
+---------------------------------+--------------------------------+
6 rows in set (0.01 sec)

3. 備份

  • 進入mysql命令列,執行 show master status;
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 |      430 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

當前正在記錄日誌的檔名是 mysql-bin.000003

  • 比如當前資料庫test的bk_user只有2條記錄
mysql> select * from test.bk_user;
+----+------+------+------+
| id | name | sex  | age  |
+----+------+------+------+
|  1 | 小明 | 男   |   25 |
|  2 | 小紅 | 女   |   21 |
+----+------+------+------+
2 rows in set (0.00 sec)
  • 插入一條新的記錄
mysql> insert into test.bk_user(name, sex, age) values('小強', '男', 24);
Query OK, 1 row affected (0.02 sec)
mysql> select * from test.bk_user;
+----+------+-----+-----+
| id | name | sex | age |
+----+------+-----+-----+
|  1 | 小明 | 男  |  25 |
|  2 | 小紅 | 女  |  21 |
|  5 | 小強 | 男  |  24 |
+----+------+-----+-----+
3 rows in set (0.03 sec)
  • 執行命令mysqladmin -uroot -p密碼 flush-logs,生成並使用新的日誌檔案

再次檢視當前使用的日誌檔案,已經變為 mysql-bin.000004 了。
mysql-bin.000003 則記錄著剛才執行的 insert 語句的日誌。

mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000004 |      154 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

到這裡,其實已經完成了增量備份了。

恢復增量備份

  • 首先假裝誤刪資料庫記錄
mysql> delete from test.bk_user where id=4;
Query OK, 1 row affected (0.01 sec)

mysql> select * from test.bk_user;
+----+------+------+------+
| id | name | sex  | age  |
+----+------+------+------+
|  1 | 小明 | 男   |   25 |
|  2 | 小紅 | 女   |   21 |
+----+------+------+------+
2 rows in set (0.00 sec)
  • 從備份的日誌檔案mysql-bin.000003中恢復資料
[[email protected] ~]# mysqlbinlog --no-defaults /var/lib/mysql/mysql-bin.000003 | mysql -uroot -p test
Enter password: 
ERROR 1032 (HY000) at line 36: Can't find record in 'bk_user'

如果你也遇到這個問題的話,不妨修改 /etc/my.cnf 配置試試。
我在server_id那一行下添加了 slave_skip_errors=1032 ,然後就執行成功了,不再報錯。

mysql> select * from test.bk_user;
+----+------+------+------+
| id | name | sex  | age  |
+----+------+------+------+
|  1 | 小明 | 男   |   25 |
|  2 | 小紅 | 女   |   21 |
|  5 | 小強 | 男   |   24 |
+----+------+------+------+
3 rows in set (0.00 sec)

至此增量備份就算成功了,需要注意的是:增量備份只是備份了更新(插入、修改、刪除)的操作,並不會備份刪表、創表這種操作的。

增量備份的shell指令碼

#!/bin/bash

# 增量備份時複製mysql-bin.00000*的目標目錄,提前手動建立這個目錄
BakDir=/home/mysql/backup/daily
# 日誌檔案
LogFile=/home/mysql/backup/bak.log

# mysql的資料目錄
BinDir=/var/lib/mysql-bin
# mysql的index檔案路徑,放在資料目錄下的
BinFile=/var/lib/mysql-bin/mysql-bin.index

# 這個是用於產生新的mysql-bin.00000*檔案
/usr/local/mysql/bin/mysqladmin -uroot -ptencns152 flush-logs

Counter=`wc -l $BinFile | awk '{print $1}'`
NextNum=0
# 這個for迴圈用於比對$Counter,$NextNum這兩個值來確定檔案是不是存在或最新的
for file in `cat $BinFile`
do
        base=`basename $file`
        NextNum=`expr $NextNum + 1`
        if [ $NextNum -eq $Counter ]
        then
                echo $base skip! >> $LogFile
        else
                dest=$BakDir/$base
                #test -e用於檢測目標檔案是否存在,存在就寫exist!到$LogFile去
                if(test -e $dest)
                then
                        echo $base exist! >> $LogFile
                else
                        cp $BinDir/$base $BakDir
                        echo $base copying >> $LogFile
                fi
        fi
done

echo `date +"%Y年%m月%d日 %H:%M:%S"` $Next Bakup succ! >> $LogFile

定時備份

執行命令 crontab -e,新增如下配置

# 每個星期日凌晨3:00執行完全備份指令碼
0 3 * * 0 /bin/bash -x /root/bash/Mysql-FullyBak.sh >/dev/null 2>&1

# 週一到週六凌晨3:00做增量備份
0 3 * * 1-6 /bin/bash -x /root/bash/Mysql-DailyBak.sh >/dev/null 2>&1

遇到的問題

  • Can't connect to local MySQL server through socket '/tmp/mysql.sock'
mysqladmin: connect to server at 'localhost' failed
error: 'Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)'
Check that mysqld is running and that the socket: '/tmp/mysql.sock' exists

去修改mysql的配置檔案,新增

[mysqladmin]
# 修改為相應的sock
socket=/var/lib/mysql/mysql.sock
  • 執行mysqldump時遇到 Unknown table 'column_statistics' in information_schema (1109)
[[email protected] bash]# /usr/local/mysql/bin/mysqldump -uroot -ptencns152 --quick --events --all-databases --flush-logs --delete-master-logs --single-transaction > /home/mysql/backup/1.sql  
mysqldump: [Warning] Using a password on the command line interface can be insecure.
mysqldump: Couldn't execute 'SELECT COLUMN_NAME,                       JSON_EXTRACT(HISTOGRAM, '$."number-of-buckets-specified"')                FROM information_schema.COLUMN_STATISTICS                WHERE SCHEMA_NAME = 'atd' AND TABLE_NAME = 'box_info';': Unknown table 'column_statistics' in information_schema (1109)

如果使用MySQL 8.0+版本提供的命令列工具mysqldump來匯出低於8.0版本的MySQL資料庫到SQL檔案,會出現Unknown table 'column_statistics' in information_schema的錯誤,因為早期版本的MySQL資料庫的information_schema資料庫中沒有名為COLUMN_STATISTICS的資料表。

解決問題的方法是,使用8.0以前版本MySQL附帶的mysqldump工具,最好使用待備份的MySQL伺服器版本對應版本號的mysqldump工具,mysqldump可以獨立執行,並不依賴完整的MySQL安裝包,比如在Windows中,可以直接從MySQL安裝目錄的bin目錄中將mysqldump.exe複製到其他資料夾,甚至從一臺電腦複製到另一臺電腦,然後在CMD視窗中執行。

當前使用是的MySQL 5.7.22。把5.7.20的 MYSQL_HOME/bin/mysqldump 替換掉 5.7.22的,接著就能順利執行mysqldump了,也真是奇了怪了。