文章目录
1 InnoDB存储引擎中的锁
1.1 锁的类型
1.1.1 行级锁
- 共享锁(S Lock),允许事务读一行数据
- 排它锁(X Lock),允许事务删除或更新一行数据
1.1.2 意向锁(表级锁)
使事务可以在更细粒度上进行加锁,使多粒度(行锁和表锁)的锁并存。
-
意向共享锁(IS Lock),事务想要获得一张表中某几行的共享锁
-
意向排他锁(IX Lock),事务想要获得一张表中某几行的排他锁
1.1.3 锁类型的总结
- X锁对任何所不兼容,排他锁嘛。
- 各意向锁相互兼容。
- IX锁对S锁不兼容。
若将上锁的对象看成一棵树,那么对最下层的对象上锁,也就是对最细粒度的对象进行上锁,那么首先需要对粗粒度的对象上锁。
举个例子,当事务A需要对某行记录加X锁,发现该表已被另一个事务B上了S锁,那么事务A需要对表上IX锁,但是IX和S锁不兼容,所以事务A需要等事务B释放表的S锁后才能上IX锁,进而再上行的X锁。
再举个例子,当事务A需要对行记录加S/X锁,发现该行已被另一个事务B上了IX锁,那么事务A可以对表上IS/IX锁,又发现该行被事务B上了行X锁,这时需要等事务B释放了行X锁后,事务A才能对行上S/X锁。
上行锁前,会检查其表锁情况,检查兼容性,进而再根据情况选择上意向锁还是等待,最后根据行锁兼容性进行加锁还是等待。
1.2 查看锁、事务及阻塞事务
在INFORMATION_ SCHEMA 架构下添加了表INNODB_ TRX、INNODB LOCKS、INNODB_ LOCK_ WAITS。通过这三张表,用户可以更简单地监控当前事务并分析可能存在的锁问题。
1.3 一致性非锁定读
指InnoDB通过行多版本控制的方式读取数据库中的数据。读取一个快照数据库数据。
之所以称其为非锁定读,因为不需要等待访问的行上X锁的释放。
一个行记录可能不止一个快照数据,称这种技术为行多版本技术。由此带来的并发控制,称之为多版本并发控制(Multi Version Concurrency Control, MVCC)。
该实现是通过undo段来完成,而undo用来在事务中回滚数据,因此快照数据本身是没有额外的开销。
通常不带任何锁语句的select,都是一致性非锁定读。
不同隔离级别对MVCC的定义不同:
-
在RC级别下,事务中读取最新的一份快照数据,会被其他事务的提交所影响。
例如,当另一个事务提交事务后,当前事务在另一个事务提交前后两次查询数据不一致,即存在不可重复读现象。
-
在RR级别下,读取事务开启前的快照数据,不随被其他事务影响。
1.4 一致性锁定读
即对读(select)操作加锁,包括两种方式:
- select for update,对行加X锁
- select lock in share mode,对行加S锁
一致性锁定读必须在一个事务中进行,当事务提交或回滚时才释放。
因此在使用上述两句SELECT锁定语句时,务必加上
BEGIN, START TRANSACTION或者SET AUTOCOMMIT=0。
2 锁的算法
2.1 行锁的3种算法
- Record Lock,单行上锁
- Gap Lock,间隙锁,锁定一个范围
- Next-Key Lock,为Record Lock+Gap Lock,锁定一个范围,并且锁定记录本身。其设计的目的是为了解决幻读问题。后面介绍。
**InnoDB在RR级别对于行的查询都是采用Next-Key Lock锁定算法。**而在RC下,仅使用Record Lock。
Next-Key Lock锁定算法根据where条件选择锁定的是范围还是单行。
对于二级索引的锁定读查询语句,会使用Gap锁锁定该索引的当前记录、上一个范围和下一个范围。
如果不锁定当前where条件索引的行记录,当另一个事务插入一个相同条件的记录时,该事务再次通过相同条件查询出的行记录数不一致,导致的幻读现象。
2.2 解决幻读问题
RR级别下,InnoDB同样会有幻读的问题,但是可以采用Next-Key Lock机制来避免幻读问题;不同于Oracle数据库,需要在Searializable级别来才能解决幻读问题。
幻读问题是指在同一事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次的SQL语句可能会返回之前不存在的行。
与不可重复读不同,不可重复读的问题,是指两次SQL语句的结果中行记录数相同,但是字段值不一致。
InnoDB中RR级别避免幻读问题的原理:事务中使用一致性锁定读的方式,在Next-Key Lock加持下,对范围行加上Gap Lock的X锁,保证其他事务无法在该范围内插入,从而避免幻读。
如果不使用一致性锁定读的方式,依然会存在事务中幻读的现象。
3 阻塞
InnoDB中,innodb_lock_wait_timeout用来控制等待的时间,默认是50s,innodb_rollback_on_timeout是否对等待超时的事务进行回滚操作,默认是OFF,代表不回滚。
注意,在一个事务中,因为等待另一个事务而导致的阻塞,当阻塞超时时抛出异常,而该事务阻塞前的所有语句,默认将被提交。
4 死锁
死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的-种互相等待的现象。
InnoDB使用wait-for Graph等待图来进行死锁检测。等待图采用胜读优先泛实现,InnoDB1.2采用非递归方式取代递归实现,进一步提高了InnoDB的性能。
在事务中,包含两个以上的如下SQL语句select for update、update和delete,则可能存在死锁问题。
5 锁升级
在Microsoft SQL Server数据库中,由于锁是一种稀有的资源,因此锁升级会带来一定的效率提高。但是锁升级带来的一个问题却是因为锁粒度变大而导致并发性能的降低。
InnoDB不存在锁升级,其不是根据每个记录来产生行锁的,其根据每个事务访问的每个页对锁进行管理的,采用的是位图的方式。
参考
《MySQL技术内幕 InnoDB存储引擎》第2版 姜承尧
原创文章转载请注明出处
MySQL中的锁
作者:坏蛋damn