MySQL InnoDB 锁

1 介绍

行级锁不一定会增加开销。InnoDB存储引擎不需要锁升级,因为一个锁和多个锁的开销是相同的。
位图存储,所以相同

InnoDB提供一致性非锁定读、行级锁支持。

lock与latch

  • latch一般被称为闩锁,是轻量级锁,要求锁定时间非常短,分为mutex互斥量、rwlock读写锁,保证并发线程操作临界资源的正确性,通常无死锁检测机制。我理解是对数据库本身各种线程请求资源加的锁
  • lock的对象是事务,锁定数据库中的对象,如表、页、行,有死锁机制。
lock latch
对象 事务 线程
保护 数据库内容 内存数据结构
持续时间 整个事务过程 临界资源
模式 行锁、表锁、意向锁 读写锁、互斥量
死锁 通过waits-for graph、time out等机制进行死锁检测和处理 无死锁检测和处理机制。仅通过应用程序加锁的顺序保证无死锁情况发生
模式 Lock Manager的哈希表中 每个数据结构的对象中

2. InnoDB存储引擎中的锁

2.1 锁的类型

InnoDB存储引擎实现了两种标准行级锁(S/X)

  • 共享锁(S Lock),允许事务读一行数据
  • 排它锁(X Lock),允许事务删除或更新一行数据

两种锁的兼容性

X S
X 不兼容 不兼容
S 不兼容 兼容

不兼容是指a事务已经获取某行的X锁,b事务获取该行的X锁时会被阻塞

InnoDB支持意向锁(Intention Lock)
意向锁将锁定对象分为多个层次,意味着事务希望在更细粒度的对象上加锁。比如对象层次分为库->表->页->行,想对行上加锁,会先对库表页上加意向锁。

X S IS IX
X 不兼容 不兼容 不兼容 不兼容
S 不兼容 兼容 兼容 不兼容
IS 不兼容 兼容 兼容 兼容
IX 不兼容 不兼容 兼容 兼容

意向锁之间是相互兼容的,因为他们的下一层(实际锁对象)之间没有冲突。

2.2 一致性非锁定读

一致性非锁定读是指InnoDB通过行多版本控制读取数据发现读取的行正在执行DELETE或UPDATE操作,则不会等待锁释放,而是读取快照数据。

特点:

  • 不需要等待锁定行的锁释放,也不会对历史数据上锁,提高并发度
  • 通过undo段(用户事务回滚)完成,因此快照数据无额外开销

在事务隔离级别READ COMMITTED和REPEATABLE READ下,InnoDB使用一致性非锁定读。

  • READ COMMITTED读取最新的快照数据
  • REPEATABLE READ读取事务开始时的行数据版本

2.3 一致性锁定读

由上节可知,某些隔离级别下的普通select语句都是一致性非锁定读,但是某些情况下需要对select操作进行一致性锁定读,InnoDB支持两种锁定读操作:

  • select … for update (悲观锁,X写锁)
  • select … lock in share mode (乐观锁,S读锁)

2.4 MVCC多版本并发控制

MVCC(Multi Version Concurrency Control),是一致性非锁定读用于不加锁读取数据,提高并发性的手段
MVCC使用的读是快照度,也就是普通的select语句。快照读不加锁,可能读到历史数据。
对于undo log、readview的讲解可以看这篇:https://www.cnblogs.com/qdhxhz/p/15750866.html

3 锁的算法

3.1 行锁的三种算法

  • Record Lock: 单行记录锁,锁某一行
  • Gap Lock:间隙锁,锁一个范围,不包含记录本身
  • Next-Key Lock:Record Lock+Gap Lock,锁定范围加本身

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是为了阻止多个事务将记录插到同一范围内,能解决不可重复读的问题

4 锁的问题

  • 脏读:一个事务读到了另一个事务未提交的数据
  • 不可重复读:一个事务内多次相同的查询语句查到的结果不同,因为其他事务提交的数据影响了当前事务
  • 丢失更新:一个事务的更新操作被另一个事务的更新覆盖。数据库层面下不会发生这种问题,因为任何隔离级别都对数据操作都会加锁

5 死锁

概念:两个或两个以上的事务在执行过程中,因争抢锁资源造成互相等待导致无法前进。

死锁的两种解决方式:超时、死锁检测

5.1 超时

设置某事务等待超过某时间后,进行回滚,释放资源。通过innodb_lock_wait_timeout设置超时时间

  • FIFO顺序回滚
  • 更新行数较多,占用undo log较大,回滚花费时间可能较长

5.2 死锁检测

wait-for graph 等待图,一种主动的死锁检测机制,通过锁信息链表和事务等待链表构造图。

5.3 两种方式优缺点

  • 超时:
    • 缺点:超时时间难以把控,时间过长系统难以接受,时间过短容易误伤正常的事务。回滚事务undo log日志可能较多。
    • 优点:底层逻辑简单,保底手段
  • 死锁检测:
    • 缺点:一旦阻塞就会检测,大量事务更新同key可能会浪费大量CPU资源进行死锁检测
    • 优点:存在死锁时回滚undo log少的事务

热点更新的问题:
热点更新会造成死锁检测花费大量CPU时间进行死锁检测,降低事务并发度。
解决方式:

  • 关闭死锁检测,依靠超时解决:可能大量超时,影响系统性能
  • 控制并发度,在应用层或中间件控制同key串行更新

6 锁升级

InnoDB不存在锁升级的问题,其不是根据记录产生行锁,而是对每个页进行锁管理,采用位图的方式。因此锁页还是锁行开销是相同的。

参考

  • 《MySQL技术内幕》 第六章
  • MVCC详解,深入浅出简单易懂
  • https://www.cnblogs.com/qdhxhz/p/15750866.html
  • https://www.jianshu.com/p/bdae82978b4d

你可能感兴趣的:(#,存储,mysql,java,数据库)