Mysqldump由于是逻辑备份,所以并不需要关心底层的存储引擎,不仅适用于支持事务的存储引擎,对于非事务引擎同样有效。但是非事务引擎需要数据库备份人员根据实际业务能够承受的停服时间,平衡对数据一致性的需求,选择合适粒度的锁,例如表锁、库锁甚至实例锁。不同粒度的锁对于服务的可用性也是不一样的。
Xtrabackup作为目前针对Innodb存储引擎使用最为广泛的一个开源备份工具,往往被解读为是无锁的备份,即整个备份过程中都不影响用户读写服务的。但是一个数据库中,即使所有的业务表都是innodb表,但是还存在一些mysql系统库下的user表等,均是myisam表(MySQL 5.5),同时备份过程需要获取Binlog文件名和位置,也要保证表定义文件的一致性,所以从整个实例的角度,即使用XtraBackup还是有一段时间需要执行Flush table with read lock全局锁的,会对用户访问产生影响,同时由于Flush table with read lock的一些特殊性,如果稍不注意,可能会对用户访问数据库产生致命影响。
MySQL官网文档对Flush tables with read lock的解释:
Closes all open tables and locks all tables for all databases with a global read lock.
If the thread that is doing FLUSH TABLES has a lock on some tables, it will first close the locked tables, then wait until all other threads have also closed them, and then reopen them and get the locks. After this it will give other threads a chance to open the same tables.
从上面的这段话,我们可以得到两个结论:
Flush tables with read lock 会上一个实例级别的全局锁,该锁与Lock tables或者Select for update等互斥,即如果前面有上述锁,会导致Flush tables with read lock阻塞。
Flush tables with read lock首先需要关闭所有的表,然后再打开所有的表。如果有线程正在扫描表,Flush tables with read lock会被阻塞,一直等到该表允许被关闭为止。如果有一个select count(*)的慢查询,会阻塞Flush tables with read lock。
可能大家认为Flush tables with read lock仅仅是一把读锁,即使阻塞了也不会影响正常的读写,但是事实却大相径庭。Peter Zaitsev 在文献【1】中提到了Flush tables with read lock的潜在风险。概括的讲,如果Flush table with read lock执行完毕,成功获取到了全局实例锁,后续的快照读和S锁的读是没有问题的,只是阻塞DDL、写;但是如果一旦因为表无法关闭或者因为其他的锁导致无法正常获取到表锁使得Flush table with read lock 阻塞,这个后果将是灾难性的,所有的读,无论是快照读,还是S锁或者X锁的读,均会被阻塞,因为Flush table with read lock需要关闭表,这点是需要所有数据库运维人员警惕的,该问题会导致线上数据库服务全部不可用。
下面我们做一个实验来验证这个问题:
首先通过lock tables 锁定某个innodb表。
然后在另外一个客户端执行Flush tables with read lock!我们看到Flush table with read lock已被阻塞。
接着在另外一个客户端执行一次快照读。
我们看到普通的一个快照读也被阻塞了。
从文献【2】XtraBackup的Release Notes中,我们可以发现,XtraBackup从1.4到2.1.3均存在这样的隐患。在2.1.4的Release Notes中我们找到了相关描述:
Percona XtraBackup has introduced additional options to handle the locking during the FLUSH TABLES WITHREAD LOCK.These options can be used to minimize the amount of the time when MySQL operates in the read-only mode.
显然,Percona已经意识到了这个问题的严重性,提供了两种解决问题的思路:
设置超时时间:XtraBackup 在执行Flush table with read lock前会检查当前MySQL的连接,一旦发现有用户请求,且用户请求为设置的需要等待的SQL语句类型,并且该SQL已经超过了设定的执行时间,则Xtrabackup等待该SQL语句执行结束才执行Flush table with readlock,直至超时返回错误。下列参数提供该功能的支持:--lock-wait-timeout=SECONDS, 用于设置Xtrabackup等待的超时时间。 --lock-wait-query-type=all|update,用于指定哪类的SQL语句是需要Flush table with read lock等待的,同时用户可以通过--lock-wait-threshold=SECONDS设置SQL语句的等待阈值,只有超过该阈值的SQL,Xtrabackup才会等待,否则直接上实例锁。
kill 其他阻塞线程:Kill掉所有阻塞Flush table with read lock的线程:--kill-long-queries-timeout=SECONDS参数允许用户指定了超过该阈值时间的查询会被Kill,同时也允许用户指定Kill SQL语句的类型。--kill-long-query-type=all|select 默认值为ALL,如果选择Select,只有Select语句会被Kill,如果Flush table with read lock是被Update语句阻塞,则XtraBackup不会处理。
[1] http://www.mysqlperformanceblog.com/2012/03/23/how-flush-tables-with-read-lock-works-with-innodb-tables
[2]http://www.percona.com/doc/percona-xtrabackup/2.1/release-notes.htm
相关阅读:MySQL 常用备份工具探析(上篇)
网易云新用户大礼包:https://www.163yun.com/gift
本文来自网易实践者社区,经作者郭忆授权发布。