MySQL 的锁 (一)
写在前面
其实 MySQL 中存在各种各样的锁,但其实由于分类的标准不一样,说法也各有不同。我在这篇文章中介绍的 MySQL 的锁是以锁的粒度区分的。
MySQL 的锁种类
全局锁
对整个数据库实例加锁,命令是 Flush tables with read lock。使这个库处于只读状态,数据更新语句(DML)、数据定义语句(DDL)以及更新类事务的提交语句都会被阻塞。
全局锁的典型应用场景是做全库逻辑备份,官方自带的逻辑备份工具 mysqldump。
mysqldump 使用-single-transaction 的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。由于 MVCC 的支持,这个过程数据是可以正常更新。
mysqldump 中重要的点(只适用于使用 innoDB 引擎的库):
FLUSH /*!40101 LOCAL */ TABLES
FLUSH TABLES WITH READ LOCK
执行完 1,再执行 2 的目的就是为了避免长事务操作造成 FLUSH TABLES WITH READ LOCK 操作获取不到锁,同时又阻塞其他客户端的操作。
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ
设置当前事务的事务隔离级别为 RR,避免不可重复读和幻读。
START TRANSACTION /*!40100 WITH CONSISTENT SNAPSHOT */
start transaction 和 start transaction with consistent snapshot 区别
1)start transaction 时,是第一条语句的执行时间点,就是事务开始的时间点,第一条 select 语句建立一致性读的 snapshot;
2)start transaction with consistent snapshot 时,
则是立即建立本事务的一致性读 snapshot,同时开启事务
UNLOCK TABLES
释放全局锁
为什么不使用 set global readonly=true 的方式使全库只读?
readonly 参数用于主从库判断逻辑使用
全局锁在客户端发生异常后,会自动释放;而将整个库设置成 readonly,会导致整个库不可写
表级锁
表锁
语法:lock tables XXX read/write
元数据锁(MetaData Lock)
MDL 不需要显式使用,在访问表的时候会被自动加上。
MySQL5.5 引入 MDL,当对一个表做增删改查操作时,加 MDL 读锁;当要对表做结构变更操作的时候,加 MDL 写锁。(读锁之间不互斥;读写锁和写锁之间是互斥的)
如何安全地给表加字段?
解决事务占据 MDL 读锁不释放的问题;alter table 里面设定等待时间,避免事务阻塞。
行级锁
innoDB 行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,innoDB 才使用行锁,否则使用表级锁。
在 innoDB 事务中,行锁是在需要的时候才加上,在事务结束时才释放的。可以提供较高的并发,但存在死锁问题-----两阶段锁协议:如果事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
一次性锁协议:在事务开始时,一次性申请所有的锁。在事务结束时,一次性释放所有的锁。不存在死锁问题。
出现死锁的解决策略:
直接进入等待,直到超时。由 innodb_lock_wait_timeout 设置
主动发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。由 innodb_deadlock_detect 设置,为 on 表示开启死锁检测。
总结
相信大家还听说过 MySQL 其他的锁,比如乐观锁、悲观锁、间隙锁等。其他这就是另外的分类标准了。我也会在后续详细介绍。
版权声明: 本文为 InfoQ 作者【技术小生】的原创文章。
原文链接:【http://xie.infoq.cn/article/f656ccdec08d3e152a37007be】。文章转载请联系作者。
评论