王小川|MySQL强人“锁”难《死磕MySQL系列 三》( 二 )


也就是说 , 在备份不加锁的话 , 不同表之间的执行备份的顺序不同 , 如果某个表在备份的过程中进行了更新并且成功备份而关联的表已经备份完成无法再进行跟新 , 此时就会出现数据不一致 。
在MVCC那篇文章中提到了一个非常重要的概念一致性视图(read view) , 一致性视图是根据快照读那一刻所有未提交事务的集合 , 前提是隔离级别为可重复 。
这时你应该知道要说什么了 , 没错就是官方大大给提供的逻辑备份工具mysqldump 。
mysqldump的备份原理是通过协议连接到 MySQL 数据库 , 将需要备份的数据查询出来 , 将查询出的数据转换成对应的insert 语句 , 当我们需要还原这些数据时 , 只要执行这些 insert 语句 , 即可将对应的数据还原 。
例如备份test库的命令为mysqldump -uroot -p test > /backup/mysqldump/test.db
当mysqldump使用参数--single-transaction时 , 备份数据之前会启动一个事物 , 拿到一致性视图(read view) , 所以在整个备份的过程中是支持更新的 。
既然有了官方大大提供的mysqldump工具为何还要使用flush tables with read lock来将整表锁住呢?
别忘记了刚提到的可以在备份过程中进行更新 , 可以更新的前提是可以得到一致性视图 , 获取一致性视图的前提是开启事务 。 这里你应该清楚 , 不是所有存储引擎都支持事物 。
如果有的表使用了别的存储引擎不支持事物 , 那么就只能使用flush tables with read lock方法 , 说到这里希望大家尽量在创建表时都选择Innodb存储引擎 。
看着好一会了 , 还能记得咱们要干什么吗?需求是全库处于只读状态 。
如果你搭建过MySQL的主从架构 , 就会知道主库用来写数据 , 从库用来读数据并且从库不支持写入操作 , 可以实现这样的效果都是来自于参数readonly 。
同样执行set global readonly=true也可以达到整库只读状态 , 那么为什么从一开始没有给大家说这个方案 , 那是有原因的 。
一是 , 刚刚提到的搭建主从架构需要使用readonly来判断主库与从库 。
二是 , 在异常处理的方式不同 。 如果使用flush talbes with read lock命令客户端异常后MySQL会自动释放全局锁 , 让整个库回到正常状态 。 而整库设置为readonly后 , 一旦发生异常就会一直处于只读状态 , 导致整库长时间处于不可写状态 。
所以说数据库一旦加上全局锁后数据的增删改、修改表结构、修改字段等操作都会被锁住 。
三、表锁表锁跟全局锁释放的命令一致unlock tables , 同样客户端断开的时候也会自动释放 。
在老一辈的革命前辈处理并发都是用的表锁 , 应该都知道锁表的影响虽不及锁库影响大 , 但在今天锁的粒度已经支持到行锁了(前提是使用Innodb存储引擎 , 就没必要再使用行锁来处理并发了 。
再来看表锁中的另一位哥们“元数据锁”(metalock)简称“MDL” , 这个锁估计很少人知道 , 因为在实际开发过程中是不会有实际的语法来开启或关闭 。
这个特性是在MySQL5.5版本后引入的 , 就是为了解决A线程正在查询一个表的数据 , 在这期间B线程修改了表的数据结构 , 那么就会造成查询的结果跟表结构对不上 , 这肯定是不行的 。
当你访问一个表时会默认加上MDL写锁 , 不管在任何时候记住读锁与读锁之间不互斥 , 读锁与写锁 , 写锁与写锁之间互斥 , 知道行锁的共享锁、排它锁也是这么个理 。
那么MDL 不需要显示调用 , 那它是在什么时候释放的?