写点什么

MySQL 系列文章 --- 初识 MySQL 中的锁

  • 2022 年 3 月 11 日
  • 本文字数:2752 字

    阅读完需:约 9 分钟

MySQL系列文章---初识MySQL中的锁

MySQL 系列文章---初识 MySQL 中的锁


前言

MySQL 中有很多种存储引擎,不同的存储引擎对应的不同的锁机制,如 MyISAM 和 MEMORY 存储引擎采用的是表级锁(Table-Level Locks);BDB 存储引擎采用的是页面锁(Page-Level Locks),但也支持表级锁;InnoDB 存储引擎既支持行级锁(Row-Level Locks),也支持表级锁,还有单列索引、Gap Locks(间隙锁)、Next-Key Locks 锁。但默认情况下是采用行级锁。其中 InnoDB 和 MyISAM 是存储引擎中最常用的两种。


提示:以下是本篇文章正文内容,下面案例可供参考

一、按照锁的粒度划分

1.1 行锁 (Row-Level Locks)

简单的来说,行锁就是一锁锁一行或者多行记录,MySQL 的行锁是基于索引加载的 ,所以行锁是要加在索引响应的行上,即命中索引。


行锁的优点:


  • 高并发时候锁冲突小

  • 事务失败,回滚代价小

  • 可以长时间锁一行


行锁的缺点:


  • 会出现死锁

  • 比表锁和页锁消耗内存更多,开销更大,请求更慢


代码示例,给指定的行加锁:


select * from A where id > 6 for update
复制代码

1.1.1 记录锁 (Record Locks)

记录锁总是锁定索引记录,即使定义的表没有索引。对于这种情况, InnoDB 创建一个隐藏的聚集索引并将该索引用于记录锁定。

1.1.2 间隙锁 (Gap Locks)

间隙锁是在索引记录之间的间隙上的锁,或在第一条索引记录之前或最后一条索引记录之后的间隙上的锁,不包括记录本身,可以防止同一个事物的两次当前读出现的幻读问题。间隙可能跨越单个索引值、多个索引值,甚至是空的。使用唯一索引锁定行以搜索唯一行的语句不需要间隙锁定。如果 id 没有索引或具有非唯一索引,则该语句会锁定前面的间隙。这里还值得注意的是,不同的事务可以在间隙上持有冲突的锁。例如,事务 A 可以在一个间隙上持有一个共享间隙锁(gap S-lock),而事务 B 在同一个间隙上持有一个排他性间隙锁(gap X-lock)。允许冲突间隙锁的原因是,如果从索引中清除记录,则必须合并不同事务在记录上持有的间隙锁。

1.1.3 临键锁 (Next-Key Locks)

临键锁是索引记录上的记录锁和索引记录之前的间隙上的间隙锁的组合。InnoDB 存储引擎中执行行级锁定的方式是,当它搜索或扫描表索引时,它会在它遇到的索引记录上设置共享或排他锁。因此,行级锁实际上是索引记录锁。索引记录上的 next-key 锁定也会影响该索引记录之前的“ gap ”。也就是说,next-key 锁是索引记录锁加上索引记录前面的间隙上的间隙锁。如果一个会话在索引中的记录 R 上具有共享或排他锁 ,则另一个会话不能在索引顺序记录 R 中之前的间隙中插入新的索引记录。默认情况下,InnoDB 在 REPEATABLE READ 事务隔离级别下运行。在这种情况下,InnoDB 使用 next-key 锁进行搜索和索引扫描,这可以防止幻读。

1.1.4 插入意向锁 (Insert Intention Locks)

插入意向锁是一种由 insert 操作在行插入之前设置的间隙锁类型。这个锁表明了插入的意图,如果插入到同一个索引间隙中的多个事务没有插入到间隙中的相同位置,那么它们就不需要互相等待。假设有值为 4 和 7 的索引记录。尝试分别插入值为 5 和 6 的独立事务,在获得插入行的排他锁之前,每个事务使用插入意图锁锁定 4 和 7 之间的间隙,但不会相互阻塞,因为行不冲突。

1.2 表锁 (Table-Level Locks)

表级锁是 MySQL 中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分 MySQL 引擎支持。最常使用的 MyISAM 与 InnoDB 都支持表级锁定。表级锁定分为表共享读锁(Table Read Lock)与表独占写锁(Table Write Lock)。MyISAM 表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;对MyISAM 表的写操作,则会阻塞其他用户对同一表的读和写请求;MyISAM 表的读和写操作之间,以及写和写操作之间是串行的


表锁的优点:


  • 开销小,加锁快

  • 不会出现死锁


表锁的缺点:锁定粒度大,发生锁冲突的概率最高,并发度最低。


代码示例,直接给整个表加锁:


select * from A where niu = 'a' for update
复制代码

1.3 页锁 (Page-Level Locks)

页级锁是 MySQL 中锁定粒度介于行级锁和表级锁中间的一种锁,一次锁定相邻的一组记录,BDB 存储引擎中支持页级锁。


页锁的特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

二、锁级别分类

2.1 共享锁(Shared Locks)

共享 ( S) 锁允许持有该锁的事务读取一行 。


共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。


如果事务 T 对数据 A 加上共享锁后,则其他事务只能对 A 再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。


代码示例:


SELECT … LOCK IN SHARE MODE;
复制代码

2.2 排他锁(Exclusive Locks)

排他 ( X) 锁允许持有该锁的事务更新或删除一行 。


排他锁又称写锁、独占锁,如果事务 T 对数据 A 加上排他锁后,则其他事务不能再对 A 加任何类型的锁。获取排他锁的事务可以对数据 A 进行更新或删除,其他事务不能再对 A 加任何锁,直到 T 释放 A 上的锁。


代码示例:


SELECT … FOR UPDATE;
复制代码

2.3 意向锁 (Intention Locks)

意向锁是表级锁,它指示事务稍后对表中的行需要哪种类型的锁(共享或独占)。有两种类型的意图锁:

2.3.1 意向共享锁(Intention-Shared Locks)

表示事务打算在表中的各个行上设置共享锁,说明事务在一个数据行加排他锁前必须先取得该表的 IX 锁。

2.3.2 意向排他锁(Intention-Exclusive Locks)

表示事务打算对表中的各个行设置排他锁,说明事务在一个数据行加排他锁前必须先取得该表的 IS 锁。

2.4 基本锁和意向锁的兼容性


注:如果锁与现有锁兼容,则将锁授予请求事务,但如果与现有锁冲突则不会。事务等待直到释放冲突的现有锁。如果锁请求与现有锁冲突并且由于会导致死锁而无法授予 ,则会发生错误,MySQL 会有报错信息(可以使用对应的存储引擎监视器,如 InnoDB 监视器 输出 SHOW ENGINE INNODB STATUS 命令查看信息)。


三 自增锁(AUTO-INC Locks)

AUTO-INC 锁是一种特殊的表级锁,由插入到带有 AUTO_INCREMENT 列的表中的事务获取。在最简单的情况下,如果一个事务向表中插入值,那么任何其他事务都必须等待自己向表中插入值,以便由第一个事务插入的行接收到连续的主键值。

四 断言锁(Predicate Locks)

InnoDB 支持包含空间数据的列的空间索引,为了处理涉及空间索引的操作的锁,临键锁不能很好地支持 REPEATABLE READ 或 SERIALIZABLE 事务隔离级别。多维数据中没有绝对的排序概念,因此不清楚哪个是临键。为了支持具有空间索引的表的隔离级别,InnoDB 使用断言锁。空间索引包含最小边界矩形(MBR)值,因此 InnoDB 通过在用于查询的 MBR 值上设置一个断言锁来强制对索引进行一致的读取。其他事务不能插入或修改匹配查询条件的行。

总结

以上就是我对 MySQL 锁的一些简单理解


著作权归 NoLongerConfused 所有。商业转载请联系 NoLongerConfused 获得授权,非商业转载请注明出处。

发布于: 刚刚阅读数: 3
用户头像

相信奇迹的人本身就和奇迹一样了不起。 2022.01.05 加入

每个人,都可以因为读了一本书,或者一段经历,生活开始有了一些不一样。

评论

发布
暂无评论
MySQL系列文章---初识MySQL中的锁_3 月月更_NoLongerConfused_InfoQ写作平台