MySQL锁机制
Contents
锁机制
InnoDB使用不同的锁策略(Locking Strategy)以及MVCC机制来实现不同的隔离级别。
-
读未提交(Read Uncommitted)
事务在读数据的时候不对数据加锁。
事务在修改数据的时候只对数据增加行级共享锁。
-
读已提交(Read Committed)
普通读是快照读,这是一种不加锁的一致性读,底层使用MVCC实现。
加锁的select, update, delete等语句,除了在外键约束检查(foreign-key constraint checking)以及重复键检查(duplicate-key checking)时会封锁区间,其他时刻都只使用记录锁。
-
可重复读(Repeatable Read)
普通的select使用快照读。
加锁的select(select…in share mode/select…for update),update,delete等语句,它们的锁,依赖于它们是否在唯一索引上使用了唯一的查询条件,或者范围查询条件:
1)在唯一索引上使用唯一的查询条件,会使用记录锁,而不会封锁记录之间的间隔,即不会使用间隙锁与临键锁。
2)范围查询条件为非唯一值时,会使用临键锁,锁住索引记录之间的范围,避免范围间插入记录,以避免产生幻读,以及避免不可重复读。
-
串行化(Serializable) 这种事务隔离级别下,所有select语句会被隐式的转化为select…in share mode。
这会导致,如果有未提交的事务正在修改某些行,所有读取这些行的操作都会被阻塞。
锁分类
-
从性能上分为乐观锁(用版本对比来实现)和悲观锁
-
从对数据库操作的类型分,分为读锁和写锁(都属于悲观锁)
读锁(共享锁,S锁(Shared)):针对同一份数据,多个读操作可以同时进行而不会互相影响
写锁(排它锁,X锁(eXclusive)):当前写操作没有完成前,它会阻断其他写锁和读锁
-
从对数据操作的粒度分,分为表锁和行锁
表锁:每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;一般用在整表数据迁移的场景。
无索引行锁会升级为表锁(RR级别会升级为表锁,RC级别不会升级为表锁)。
行锁
每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。
间隙锁,锁的就是两个值之间的空隙。间隙锁是在可重复读隔离级别下才会生效。
临键锁(Next-key Locks)是行锁与间隙锁的组合。
保姆级教程!2 万字 + 30 张图搞懂 MySQL 是怎么加行级锁的?
锁优化建议
-
尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
-
合理设计索引,尽量缩小锁的范围
-
尽可能减少检索条件范围,避免间隙锁
-
尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行
-
尽可能低级别事务隔离