MySQL锁(一)全局锁:如何做全库的逻辑备份?
数据库锁设计的初衷是处理并发问题,这也是数据库与文件系统的最大区别。
根据加锁的范围,MySQL里大致可以分为三种锁:全局锁、表锁和行锁。接下来我们会分三讲来介绍这三种锁,今天要讲的是全局锁。
全局锁
全局锁,顾名思义,就是对整个数据库加锁。MySQL 提供了一个加全局读锁的方法,命令是 Flush tables with read lock
(FTWRL)。
全局锁典型的应用场景是做全库的逻辑备份。
通过FTWRL加全局锁
通过FTWRL确保不会有其他线程对数据库做更新,然后对整个库做备份。注意,在备份过程中整个库完全处于只读状态。
- 如果在主库上备份。那么在备份期间都不能执行更新。
- 如果在从库上备份。那么在备份期间,从库不能执行主库同步过来的 binlog,从而造成主从延时。
在备份过程中数据库无法写,这对业务会有很大的影响,为什么要加锁呢,不加锁行不行呢?
我们来看看不加锁会怎样。以银行转账为例。
图1 备份同时在转账
当用户A转100给用户B,如果在用户A账户扣除了100,但还没给用户B账户增加100时,对账户表完成了备份,那此时备份数据与生产环境的数据出现不一致性。
也就是说,不加锁的话,备份得到的库不是一个逻辑时间点,这个视图是逻辑不一致的。当进行备份还原时,用户B账户就少了100,这就出现很严重的问题了。
通过加全局锁会影响业务,那有更好的办法吗?
在事务隔离的实现原理那一篇文章里,我们学习了事务隔离中的可重复读隔离级别是能够得到一致性视图的。而一致性视图能够确保数据的逻辑一致性。
官方自带的逻辑备份工具是 mysqldump
。当 mysqldump
使用参数 -single-transaction
的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。而由于MVCC的支持,这个过程中数据是可以正常更新的。查看这篇,了解更多关于MVCC和一致性读的内容。
有了一致性读后,还需要 FTWRL 吗?
一致性读是好,但前提是引擎要支持这个隔离级别。比如,对于MyISAM这种不支持事务的引擎,如果备份过程中有更新,总是只能取到最新的数据,那么就破坏了备份的一致性。这时,我们就需要使用FTWRL命令了。
所以 -single-transaction
方法只适用于所有的表使用事务引擎的库。
既然要全库只读,为什么不使用set global readonly=true的方式呢?
虽然 readonly 也能够设置全库为只读状态。但还是建议使用 FTWRL,原因有两个:
- 在有些系统中,readonly的值会被用来做其他逻辑,比如用来判断一个库是主库还是备库。因此,修改global变量的方式影响面更大,不建议你使用。
- 在异常处理机制上有差异。如果执行FTWRL命令之后由于客户端发生异常断开,那么MySQL会自动释放这个全局锁,整个库回到可以正常更新的状态。而将整个库设置为readonly之后,如果客户端发生异常,则数据库就会一直保持readonly状态,这样会导致整个库长时间处于不可写状态,风险较高。