InnoDB 中锁的实际应用(KeyWord:MVCC / 一致性[非]锁定读)

锁的作用:

   InnoDB 中锁的使用,一方面是为了提高 DB 的并发访问能力, 另外一方面是为了确保每个DB链接都能安全的读取和修改数据

   MyISAM 中使用的是表锁;SQL Server 也引入了行锁,但其行锁越多,占用的资源就越多;InnoDB 下的行锁不占用额外的资源,有更好的并发处理能力。

InnoDB 下的悲观锁:

锁的类型

  1. 行级锁:排他锁(x)、共享锁(s)。

  2. 页面锁:意向排他锁(ix)、意向共享锁(is)。

  3. 表级锁:意向排他锁(ix)、意向共享锁(is)。

   为了支持在不同粒度上进行加锁操作,更好地控制表级锁和行级锁之间的冲突,提高并发性能,InnoDB 支持一种行锁外的意向锁(lntention Lock) 。意向锁是将锁定的对象分为多个层次,如下图所示:
在这里插入图片描述

   在 InnoDB 中,当需要对记录 r 添加 X 锁时,首先需要对表、页添加 IX 锁,再对 r 添加 X 锁,若过程中出现冲突,那么加锁过程将被阻塞。

锁类型意向排他锁意向共享锁排他锁共享锁
意向排他锁冲突冲突
意向共享锁冲突
排他锁冲突冲突冲突冲突
共享锁冲突冲突

锁兼容性小总结:

  • 排他锁与所有的锁都不兼容;
  • 意向锁之间不会冲突;
  • 意向排他锁(IX)与X 、S 锁不能共存。

一致性非锁定读(consistent nonlocking read)

   CNR 是指 InnoDB 通过 MVCC 方式,读取不同版本的行数据,避免加锁的情景,提高 DB 的并发能力。

   在事务隔离级别 READ COMMITTED 和 REPEATABLE READ 下, InnoDB 存储引擎使用CNR 。但两种隔离级别, 对快照数据的定义不相同。在 READ COMMITTED 级别下,总是 读取行的最新版本,如果行被锁定了,则读取该行版本的最新一个快照。而在 REPEATABLE READ 级别下,总是读取事务开始时的行数据版本 (MVCC的 undo 隐藏字段来实现)。

更多细节内容参考《MySQL技术内幕InnoDB存储引擎》6.3.2

一致性锁定读(consistent locking read)

   部分情况下,需要显式地对数据库读取操作进行加锁,以保证数据逻辑的一致性。InnoDB 支持两种一致性的锁定读操作:

  • SELECT *** FOR UPDATE:对读取的行记录加一个x 锁
  • SELECT•••LOCK IN SHARE MODE:对读取的行记录加一个S 锁

注意点:

  • MySQL InnoDB引擎默认的修改数据语句:update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,如果要加排他锁可以使用select …for update语句,加共享锁可以使用select … lock in share mode语句。所以加过排他锁的数据行在其他事务中不能获取到两种锁(即不能修改数据),但可以直接通过select …from…查询数据,因为普通查询没有任何锁机制。

  • InnoDB的行锁是针对索引加的锁,不是针对记录加的锁。并且索引不能失效,否则都会从行锁升级为表锁。

更多关于MySQL中锁的介绍

锁的实现算法

InnoDB 存储引擎有 3 种行锁的算法, 其分别是

  • Record Lock : 单个行记录上的锁
  • Gap Lock : 间隙锁, 锁定一个范围, 但不包含记录本身
  • Next-Key Lock : Gap Lock+Record Lock , 锁定一个范围, 并且锁定行记录本身。InnoDB 对于行的加锁查询(X锁或S锁)都是采用这种锁定算法。

UniqueKey的加锁

   当 查询的列是完整的唯一索引列 的情况下, Next-Key Lock 可以降级为 Record Lock 。若查询的列是唯一索引列的一部分(某个字段或字段的前半部分),那么Next-Key Lock 不会被降级。
   需要注意的是,Next-Key Lock 降级为 RecordLock 后,其他事务就可以对间隙中的某个位置进行加锁操作,可能会导致并发场景下的业务问题。

辅助索引的加锁

   对于辅助索引,InnoDB 会对辅助索引的 当前 key 使用 Next-Key Lock,对辅助索引的下一区间 key 使用 Gap Lock(案例:《技术内幕InnoDB存储引擎 v2》P267),并将辅助索引 对应的聚簇索引 Key 上添加 Record Lock。

案例:

select * from table_test_gap_lock where b=5 for update
table_test_gap_lock:表中UK为a,Key为b
b=5对应的UK-a=3

对辅助索引 b=3 使用 Next-Key Lock,锁定列b的前后区间;且对聚簇索引 a=3 添加 Record Lock。

Next-Key Lock 加上 MVCC,就可以解决 Repeatable Read 下幻读的问题。更多 幻读问题参考

MVCC

作用:

   Multi-Version Concurrency Control(多版本并发控制机制),应用在 Repeatable-Read 和 Read Committed 隔离级别下,用于控制并发读写冲突问题,也可以保证事务的隔离性和数据一致性。

实现:

   MVCC 通过3个隐式字段(DB_ROW_ID、DB_ROLL_PTR、DB_TRX_ID)、Read View、undo log来实现。

MVCC 是一种乐观锁,基于版本号设计出了数据快照的概念,在每行记录后面保存了两个隐藏的列来实现。这两个列分别保存了行的创建版本号、行的过期或删除版本号。

每个事务开始前,都有确定了自己的版本号,用来和查询到的每行记录的版本号进行比较。
(这个描述取自《高性能MySQL-第三版》,但MySQL中不是这么简单的。)

隐藏字段:
   每行数据后新增的3个隐藏字段,主要用于记录当前数据行操作的事务id(DB_TRX_ID)

Read View
   主要是用来做可见性判断,里面保存了 “当前对本事务不可见的其他活跃事务”

undo log:

  1. 当事务回滚时用于将数据恢复到修改前的样子
  2. 另一个作用是 MVCC ,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log 恢复之前版本的数据,以此实现非锁定读

参考资料:

《高性能MySQL-第三版》
《MySQL技术内幕InnoDB存储引擎》

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值