行级锁不一定会增加开销。InnoDB存储引擎不需要锁升级,因为一个锁和多个锁的开销是相同的。
位图存储,所以相同
InnoDB提供一致性非锁定读、行级锁支持。
lock与latch
lock | latch | |
---|---|---|
对象 | 事务 | 线程 |
保护 | 数据库内容 | 内存数据结构 |
持续时间 | 整个事务过程 | 临界资源 |
模式 | 行锁、表锁、意向锁 | 读写锁、互斥量 |
死锁 | 通过waits-for graph、time out等机制进行死锁检测和处理 | 无死锁检测和处理机制。仅通过应用程序加锁的顺序保证无死锁情况发生 |
模式 | Lock Manager的哈希表中 | 每个数据结构的对象中 |
InnoDB存储引擎实现了两种标准行级锁(S/X)
两种锁的兼容性
X | S | |
---|---|---|
X | 不兼容 | 不兼容 |
S | 不兼容 | 兼容 |
不兼容是指a事务已经获取某行的X锁,b事务获取该行的X锁时会被阻塞
InnoDB支持意向锁(Intention Lock)
意向锁将锁定对象分为多个层次,意味着事务希望在更细粒度的对象上加锁。比如对象层次分为库->表->页->行,想对行上加锁,会先对库表页上加意向锁。
X | S | IS | IX | |
---|---|---|---|---|
X | 不兼容 | 不兼容 | 不兼容 | 不兼容 |
S | 不兼容 | 兼容 | 兼容 | 不兼容 |
IS | 不兼容 | 兼容 | 兼容 | 兼容 |
IX | 不兼容 | 不兼容 | 兼容 | 兼容 |
意向锁之间是相互兼容的,因为他们的下一层(实际锁对象)之间没有冲突。
一致性非锁定读是指InnoDB通过行多版本控制读取数据发现读取的行正在执行DELETE或UPDATE操作,则不会等待锁释放,而是读取快照数据。
特点:
在事务隔离级别READ COMMITTED和REPEATABLE READ下,InnoDB使用一致性非锁定读。
由上节可知,某些隔离级别下的普通select语句都是一致性非锁定读,但是某些情况下需要对select操作进行一致性锁定读,InnoDB支持两种锁定读操作:
MVCC(Multi Version Concurrency Control),是一致性非锁定读用于不加锁读取数据,提高并发性的手段
MVCC使用的读是快照度,也就是普通的select语句。快照读不加锁,可能读到历史数据。
对于undo log、readview的讲解可以看这篇:https://www.cnblogs.com/qdhxhz/p/15750866.html
Next-Key Lock算法下,索引有10,11,13三个值,则索引被锁的区间可能为:(-∞,10],(10,11],(11,13],(13,+∞)
当查询的索引是唯一索引时,InnoDB会对Next-Key Lock进行优化,降级为Record Lock。
InnoDB还会对辅助索引的下一个键值加上gap lock(如对11加锁会将(10,13)整体加上锁)。
Gap Lock是为了阻止多个事务将记录插到同一范围内,能解决不可重复读的问题
概念:两个或两个以上的事务在执行过程中,因争抢锁资源造成互相等待导致无法前进。
死锁的两种解决方式:超时、死锁检测
设置某事务等待超过某时间后,进行回滚,释放资源。通过innodb_lock_wait_timeout设置超时时间
wait-for graph 等待图,一种主动的死锁检测机制,通过锁信息链表和事务等待链表构造图。
热点更新的问题:
热点更新会造成死锁检测花费大量CPU时间进行死锁检测,降低事务并发度。
解决方式:
InnoDB不存在锁升级的问题,其不是根据记录产生行锁,而是对每个页进行锁管理,采用位图的方式。因此锁页还是锁行开销是相同的。