打开general日志
set global general_log=on;
备份语句:
./mysqldump -S tmp/mysql.sock --single-transaction --master-data=2 -B db --tables db1 > tmp/db1.sql
通过查看日志的备份流程分析备份流程
1、执行FLUSH TABLES 语句把内存中的表结构同步到磁盘上;
2、然后执行FLUSH TABLES WITH READ LOCK 加一个全局S锁,为后面执行一致性备份做准备。
因为开启了--master-data=2,这时就需要flush tables with read lock锁住全库,记录当时的master_log_file和master_log_pos点
3、接下来执行SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ语句修改隔离级别为RR,在这种隔离级别下对于事务引擎INNODB可以实现一致性非锁定读,即可以得到一个一致性快照。
--single-transaction参数的作用,设置事务的隔离级别为可重复读,即REPEATABLE READ,这样能保证在一个事务中所有相同的查询读取到同样的数据,也就大概保证了在dump期间,如果其他innodb引擎的线程修改了表的数据并提交,对该dump线程的数据并无影响,然而这个还不够,还需要看下一条
4、然后执行START TRANSACTION WITH CONSISTENT SNAPSHOT语句开始一个一致性快照事务,这里在整个备份过程中是一个大事务。
这时开启一个事务,并且设置WITH CONSISTENT SNAPSHOT为快照级别(如果mysql版本高于某一个版本值,我还不大清楚40100代表什么版本)。想象一下,如果只是可重复读,那么在事务开始时还没dump数据时,这时其他线程修改并提交了数据,那么这时第一次查询得到的结果是其他线程提交后的结果,而WITH CONSISTENT SNAPSHOT能够保证在事务开启的时候,第一次查询的结果就是事务开始时的数据A,即使这时其他线程将其数据修改为B,查的结果依然是A
5、接着执行SHOW MASTER STATUS 获取到binlog文件名和位置,这个位置就是当前备份数据的binlog位置。对于事务引擎,获取到的就是快照数据的binlog位置。
6、当获取到binlog位置后,执行UNLOCK TABLE 语句解锁事务表,如果是非事务表,则在整个备份过程中会一直锁着,直到备份完成才释放。在整个步骤之后,开始循环遍历每个库、每个表进行备份,根据备份选项,如果有触发器,事件、存储过程和函数等,也会进行备份。
等记录完成后,就立即释放了,因为现在已经在一个事务中了,其他线程再修改数据已经无所谓,在本线程中已经是可重复读,这也是这一步必须在第4步START TRANSACTION WITH CONSISTENT SNAPSHOT 之后的原因,如果SHOW MASTER STATUS和UNLOCK TABLES都在第4步之前的话就不行了,因为这时事务还没开启,一旦释放,其他线程立即就可以更改数据,从而无法保证得到事务开启时最准确的pos点。
7、在一个库中的表备份开始之前会定义一个保存点,对每一个表的备份都是从整个保存点开始的,当每一个表备份完之后都会回滚到这个保存点,这样当发生问题时方便进行回滚。
当一个库下的所有表都备份完之后就会释放这个保存点。对每一个表的备份过程,需要经过这个步骤:首先查找出表结构的定义语句;然后对表执行SELECT * 语句获取全表数据;最后使用表结构中的字段信息和所获取的表数据来生成INSERT语句。
保存点以及会滚到保存点是为了释放元数据锁
ROLLBACK TO SAVEPOINT确实可以提高DDL的并发性
说明:备份innodb表之前,就已经将锁释放掉了,这实际上是利用了innodb引擎的MVCC机制,开启快照读后(set transaction isolation level repeatable read),就能获取那个时间的一致的数据,无论需要备份多长时间,直到整个事务结束(commit)为止。当然,这个RR级别的快照,对表的元数据也有要求,因为逻辑备份表是一个个进行的,如果在备份某个表之前,这个表做了DDL操作(此时备份事务暂时还没有加MDL锁),后续再备份这个表时,就会因为表定义不一致而报错(Table definition has changed, please retry transaction)。所以总地来说,通过保存点机制,可以有效减少DDL操作的限制,但是也不能完全消除由于DDL操作导致的备份失败问题。