mysqldump 是现在在小型企业使用最为广泛的 MySQL 备份工具,因为很多初级运维工程师基本上只能接触到它,所以我们就先来讲一下它。mysqldump 的原理很简单,就是 SELECT 把数据提取出来,但是结合不同存储引擎的其他特性,mysqldump 既可以实现温备,又可以实现热备。
- MySQL 版本:
5.5.56-MariaDB MariaDB Server (yum 安装)
- Linux 版本:
CentOS 7.4 x86_64
- SELinux 状态:
Permissive
- Firewalld 状态:
Stop
配置文件为默认配置文件,就添加了 binlog 的两条设置
[root@chenyanshan ~]# cat /etc/my.cnf | grep -v "^#"
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
log-bin=/opt/binlog/binlog # 设置 binlog 文件位置
binlog-format=mixed # 设置 binlog 格式
[mysqld_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid
!includedir /etc/my.cnf.d
[root@chenyanshan ~]# mkdir /opt/binlog/
[root@chenyanshan ~]# chown mysql:mysql /opt/binlog/
随便找了些数据创建了一张表。
MariaDB [test_db]> DESC test;
+--------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-----------------------+------+-----+---------+-------+
| group | mediumint(8) unsigned | NO | PRI | 0 | |
| module | char(30) | NO | PRI | | |
| method | char(30) | NO | PRI | | |
+--------+-----------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
MariaDB [test_db]> SELECT count(`group`) FROM test;
+----------------+
| count(`group`) |
+----------------+
| 2027 |
+----------------+
1 row in set (0.00 sec)
对新创建的表进行备份。
# mysqldump --databases test_db --lock-all-tables --master-data=2 > ~/test_backup.sql
在这里,我们详细的说一下上面那条命令所使用的参数:
--databases db1 db2 …
: 指定需要备份的库,如有多个请用空格隔开--lock-all-tables
: 所有需要进行备份的库都施加读锁,这样可以防止在一张表做备份的时候另一张表正在被修改。导致数据不一致,这个锁全表的操作,是可以算作为备份的开销的。--master-data={ 0 | 1 | 2 }
: 记录当前二进制日志文件名和在里面的位置,0为不记录,1为记录为非注释信息,2为记录为注释信息,一般用2
这个时候我们再对表进行一些操作。
MariaDB [test_db]> DELETE FROM test WHERE group = 5;
Query OK, 219 rows affected (0.00 sec)
MariaDB [test_db]> SELECT count(`group`) FROM test;
+----------------+
| count(`group`) |
+----------------+
| 1808 |
+----------------+
1 row in set (0.00 sec)
MariaDB [test_db]> DELETE FROM test WHERE module = "bug";
Query OK, 167 rows affected (0.00 sec)
我们假设 “WHERE group = 5” 为正常操作,“WHERE module
= “bug”” 为误操作,这个时候我们就需要回到 “WHERE group = 5” 操作后面,但是备份并没有备份到这里,所以还需要结合二进制日志进行即时点还原。
还原操作
1. 离线数据库
如果出现误操作需要还原数据库,请千万要先将数据库离线。
#socket=/var/lib/mysql/mysql.sock
socket=/var/lib/mysql/mysql_temp.sock
port=3307
即注释掉正常的 socket 文件位置(如果 APP 程序并不是和 MySQL 在同一服务器就不需要更改 socket 文件位置),并设置 MySQL 端口为非正常使用端口。当然在这样设定之后就需要手动指定 socket 位置或者 port。
2. 查看备份位置:
之前备份的时候使用了 –master-data=2 参数,现在可以看下这个参数的效果了。
[root@chenyanshan ~]# head -n200 ~/test_backup.sql | grep MASTER_LOG_FILE
-- CHANGE MASTER TO MASTER_LOG_FILE='binlog.000001', MASTER_LOG_POS=733;
这里就可以看出来 binlog 文件为 binlog.000001,log pos 为 733。
3. 查看错误位置:
# mysqlbinlog --start-position=733 /opt/binlog/binlog.000001
·····
·····
·····
# at 804
#180314 10:33:29 server id 1 end_log_pos 904 Query thread_id=2 exec_time=0 error_code=0
use `test_db`/*!*/;
SET TIMESTAMP=1521038009/*!*/;
DELETE FROM test WHERE `group` = 5
/*!*/;
# at 904
#180314 10:33:29 server id 1 end_log_pos 976 Query thread_id=2 exec_time=0 error_code=0
SET TIMESTAMP=1521038009/*!*/;
COMMIT
/*!*/;
# at 976
#180314 10:33:31 server id 1 end_log_pos 1047 Query thread_id=2 exec_time=0 error_code=0
SET TIMESTAMP=1521038011/*!*/;
BEGIN
/*!*/;
# at 1047
#180314 10:33:31 server id 1 end_log_pos 1152 Query thread_id=2 exec_time=0 error_code=0
SET TIMESTAMP=1521038011/*!*/;
DELETE FROM test WHERE `module` = "bug"
/*!*/;
这里可以发现,错误操作为 1047,而它的上一个为 976,所以我们需要回滚到 976 就行了。
4. 将 binlog 文件中正确内容导出来。
# mysqlbinlog --start-position=733 --stop-position=976 /opt/binlog/binlog.000001 > ~/binlog_backup.sql
这样我们就有了完全备份文件,和后面变更过,但是是误操作之前的文件。
5. 恢复
[root@chenyanshan ~]# mysql --socket /var/lib/mysql/mysql_temp.sock
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 2
Server version: 5.5.56-MariaDB MariaDB Server
Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> DROP DATABASE test_db; # 删除错误的数据
Query OK, 1 row affected (0.00 sec)
MariaDB [(none)]> CREATE DATABASE test_db;
Query OK, 1 row affected (0.00 sec)
MariaDB [(none)]> USE test_db;
Database changed
MariaDB [test_db]> SET SESSION SQL_LOG_BIN=0; # 防止无用的二次二进制日志空间占用
Query OK, 0 rows affected (0.00 sec)
MariaDB [test_db]> source ~/test_backup.sql # 先进行完全备份的恢复
MariaDB [test_db]> source ~/binlog_backup.sql # 再进行更改部分的恢复
MariaDB [test_db]> SELECT count(`group`) FROM test;
+----------------+
| count(`group`) |
+----------------+
| 1808 |
+----------------+
1 row in set (0.00 sec)
恢复完成之后,修改配置文件,让服务器重新上线。
至此,一次完整的备份外加即时点恢复就完成了。基本上 mysqldump 就这么些内容。这里再将一些 mysqldump 常用的参数列一下
--flush-logs
: 滚动二进制日志,一般来说用不上,具体看情况。--events
: 备份事件调度器代码--routines
: 备份存储过程和存储函数--triggers
: 备份触发器
热备
上面全部内容都只是温备。并没有涉及到热备。热备只需要一个参数就能进行,但是它需要存储引擎和事务隔离级别支持。具体来说,就是要存储引擎支持MVCC(多版本并发控制),并且事务隔离级别需要为读提交( READ COMMITTED)和可重读(REPEATABLE READ),当满足这两个需求的时候, 每个事务启动时,存储引擎会为每个启动的事务创建一个当下时刻的快照。并且让这个快照中读到的数据的版本加一的。以后只要是这个事务读的数据,一定会去找比这个版本更老的数据。这样就能读到以前的数据。这样就能实现在施加了锁之后还不影响其他用户的读写操作。MySQL 默认存储引擎 InnoDB 就支持 MVCC,Percona Server 的 XtraDB 存储引擎也支持。
--single-transaction
: 它在存储引擎支持 MVCC 和事务隔离级别也符合要求的情况下,会创建一个单一事务来保证备份的过程中数据的一致性。需要注意的是,是指定的库里面所有的表的存储引擎都支持 MACC 的时候(存储引擎是表级别的),才会热备。它和参数--lock-all-tables
冲突,建议不要一起使用。
上面提及到了MVCC和事务隔离级别,以及存储引擎等概念。相信这也是很多运维同学不是太能理解 MySQL 备份还原的原因,因为里面涉及到的不只是简简单单的操作,还有 MySQL 一些略深层次的概念。
其他
mysqldump 自身并不支持增量备份和差异备份,但是可以通过 binlog 来实现增量和差异备份。虽然做法有点low,但是是可行的。方法也比较简单。增量就是备份的时候只备份 binlog 导出来的 SQL 文件,并且开始的 POS 是全备的记录的那个。实现差异备份的方式也差不多。
当然很少有人去这么做,因为需要增量备份或者差异备份的话,Xtrabackup会更好。