宁波专业网站建设公司/近期的时事热点或新闻事件
锁的作用:
InnoDB 中锁的使用,一方面是为了提高 DB 的并发访问能力, 另外一方面是为了确保每个用户都能安全的读取和修改数据。
MyISAM 中使用的是表锁;SQL Server 也引入了行锁,但其行锁越多,占用的资源就越多;InnoDB 下的行锁不占用额外的资源,有更好的并发处理能力。
InnoDB 下的锁:
锁的类型
为了支持在不同粒度上进行加锁操作,InnoDB 支持一种行锁外的意向锁(lntention Lock) 。意向锁是将锁定的对象分为多个层次。
-
行级锁:排他锁(x)、共享锁(s)。
-
页面锁:意向排他锁(ix)、意向共享锁(is)。
-
表级锁:意向排他锁(ix)、意向共享锁(is)。
在 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 级别下,总是读取事务开始时的行数据版本。
一致性锁定读(consistent locking read)
部分情况下,需要显式地对数据库读取操作进行加锁,以保证数据逻辑的一致性。InnoDB 支持两种一致性的锁定读操作:
- SELECT *** FOR UPDATE:对读取的行记录加一个x 锁
- SELECT•••LOCK IN SHARE MODE:对读取的行记录加一个S 锁
锁的实现
InnoDB 存储引擎有 3 种行锁的算法, 其分别是
- Record Lock : 单个行记录上的锁
- Gap Lock : 间隙锁, 锁定一个范围, 但不包含记录本身
- Next-Key Lock : Gap Lock+Record Lock , 锁定一个范围, 并且锁定记录本身。InnoDB 对于行的查询都是采用这种锁定算法。
需要注意的是,当 查询的列是完整的唯一索引列 的情况下, Next-Key Lock 可以降级为 Record Lock 。若查询的列是唯一索引列的一部分,那么Next-Key Lock 不会被降级。
但若是辅助索引,InnoDB 会对辅助索引的当前 key 使用 Next-Key Lock,对辅助索引的下一个 key 使用 Gap Lock(案例:《技术内幕InnoDB存储引擎 v2》P267),并将辅助索引 对应的聚簇索引 Key 上添加 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:
- 当事务回滚时用于将数据恢复到修改前的样子
- 另一个作用是 MVCC ,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log 读取之前的版本数据,以此实现非锁定读
参考资料:
《高性能MySQL-第三版》