当前位置: 首页 > news >正文

MySQL | 行锁——记录锁、间隙锁 、临键锁、插入意向锁

1、InnoDB中的行锁

行锁(Row Lock) 也称为记录锁,顾名思义,就是锁住某一行(某条记录row)。需要注意的是,MySQL服务器层并没有实现行锁机制,行级锁只在存储引擎层实现

优点:锁定力度小,发生锁冲突概率低,可以实现的并发度高

缺点:对于锁的开销比较大,加锁会比较慢,容易出现死锁情况。

InnoDB与MyISAM的最大不同有亮点:一是支持事务;二是采用了行级锁。

1、记录锁(Record Locks)

记录锁页就是仅仅把一条记录锁上,官方的类型名称为:Lock_REC_NOT_GAP。比如把id值为1的那条记录加一个记录锁。仅仅是锁住了id值为1的记录,对其他行的数据没有影响。

记录锁是有S锁和X锁之分的,称之为S型记录锁X型记录锁

  • 当一个事务获取了一条记录的S型记录锁后,其他事务也可以继续获取该记录的S型记录锁,但不可以继续获取X型记录锁;
  • 当一个事务获取了一条记录的X型记录锁后,其他事务既不可以继续获取该记录的S型记录锁,也不可以继续获取X型记录锁。

举例:记录锁的使用场景模拟

场景1:事务1开启S锁,事务2开始S锁并且尝试获取X型记录锁

事务1:开启S锁

mysql> begin;
Query OK, 0 rows affected (0.01 sec)mysql> select * from user where id = 1 lock in share mode;
+----+---------+
| id | name    |
+----+---------+
|  1 | 张三3   |
+----+---------+
1 row in set (0.00 sec)

事务2:开启S锁,尝试获取X锁

mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> select * from user where id  = 1 lock in share mode;
+----+---------+
| id | name    |
+----+---------+
|  1 | 张三3   |
+----+---------+
1 row in set (0.00 sec)mysql> update user set name = "张三4" where id = 1;

此时事务2获取S锁成功,获取X锁失败进行阻塞状态。

场景2:事务1开启X锁,事务2尝试获取X锁和S锁

事务1:开启X锁

mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> select * from user where id  = 1 for update;
+----+---------+
| id | name    |
+----+---------+
|  1 | 张三3   |
+----+---------+
1 row in set (0.00 sec)

事务2:尝试获取X锁和S锁

mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> select * from user where id = 1 ;
+----+---------+
| id | name    |
+----+---------+
|  1 | 张三3   |
+----+---------+
1 row in set (0.00 sec)mysql> select * from user where id = 1 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> update user set name = "张三10" where id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> 

事务2获取X锁和S锁失败,进入阻塞

场景3:事务1对id值为1的进行加S锁,事务2对id值的5的加X锁。

事务1:开启S锁

mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> select * from user where id = 1 lock in share mode;
+----+---------+
| id | name    |
+----+---------+
|  1 | 张三3   |
+----+---------+
1 row in set (0.00 sec)

事务2:获取X锁

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update user set name = "王五2" where id = 5;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

事务2获取X锁成功。如果对同一行记录进行加锁则事务2获取X锁失败,对不同的行进行加锁则互不影响。

2、间隙锁 (Gap Locks)

MySQLREPEATABLE READ 隔离级别下是可以解决幻读问题的,解决方案有两种,可以使用 MVCC 方案解决,也可以采用加锁 方案解决。但是在使用加锁方案解决时有个大问题,就是事务在第一次执行读取操作时,那些幻影记录尚不存在,我们无法给这些幻影记录加上记录锁。InnoDB提出了一种称之为 **Gap Locks **的锁,官方的类型名称为: LOCK_GAP ,我们可以简称为 gap锁。比如,把id值为8的那条记录加一个gap锁的示意图如下。

id1358
name李四张三王五赵六

图中id值为8的记录加了gap锁,意味着**不允许别的事务在id值为8的记录前边的间隙插入新记录 **,其实就是id列的值(5,8)这个区间的新记录是不允许立即插入的。比如,有另外一个事务再想插入一条id值为6的新记录,它定位到该条新记录的下一条记录的id值为8,而这条记录上又有一个gap锁,所以就会阻塞插入操作,直到拥有这个gap锁的事务提交了之后,id列的值在区间(5,8)中的新记录才可以被插入。
gap锁的提出仅仅是为了防止插入幻影记录而提出的。虽然有 共享gap锁 独占gap锁 这样的说法,但是它们起到的作用是相同的。而且如果对一条记录加了gap锁(不论是共享gap锁还是独占gap锁),并不会限制其他事务对这条记录加记录锁或者继续加gap锁。

举例:间隙锁的使用场景模拟

事务1:加S锁

mysql> select * from user;
+----+------------+
| id | name       |
+----+------------+
|  1 | 张三3      |
|  3 | 张胜男2    |
|  5 | 王五2      |
|  8 | 赵六       |
| 10 | 张三10     |
+----+------------+
5 rows in set (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> select * from user where id = 6 lock in share mode;
Empty set (0.00 sec)mysql> 

事务2:加X锁

mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> select * from user where id = 6 for update;
Empty set (0.00 sec)mysql> 

事务3:新增数据

mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> insert into user (`id`,`name`) values (7,"李四33");
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted
mysql> insert into user (`id`,`name`) values (2,"李四33");
Query OK, 1 row affected (0.00 sec)

间隙锁加的是6,所以间隙范围是5-8,新增id为7则失败,新增id为2则成功。

3、临键锁 (Next-Key Locks)

有时候我们既想 锁住某条记录 ,又想 阻止其他事务在该记录前边的 间隙插入新记录,所以innoDB就提出了一种称之为Next-Key Locks的锁,官方的类型名称为: LOCK_ORDINARY,我们也可以简称为next-key锁。Next-KeyLocks是在存储引擎 innodb 、事务级别在 **可重复读 **的情况下使用的数据库锁,innodb默认的锁就是Next-Keylocks。

next-key锁 的本质就是一个 记录锁 和一个 gap锁 的合体,它既能保护该条记录,又能阻止别的事务将新记录插
入被保护记录前边的 间隙

举例:临键锁的使用场景模拟

事务1:加X锁

mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> select * from user where id <=8 and id >5 for update;
+----+--------+
| id | name   |
+----+--------+
|  8 | 赵六   |
+----+--------+
1 row in set (0.00 sec)

事务2:尝试获取X锁和S锁,并且尝试插入数据

mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> select * from user where id = 8 lock in share mode;
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted
mysql> select * from user where id = 8 for update;
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted
mysql> insert into user (id,name) values (7,"李四2323");
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted

事务2都需要进行等待,满足间隙锁和记录锁要求

4、插入意向锁(Insert Intention Locks)

我们说一个事务在 插入 一条记录时需要判断一下插入位置是不是被别的事务加了 gap锁 (next-key锁 也包含gap锁),如果有的话,插入操作需要等待,直到拥有 gap锁 的那个事务提交。但是InnoDB规定事务在等待的时候也需要在内存中生成一个锁结构,表明有事务想在某个 间隙插入新记录,但是现在在等待。InnoDB就把这种类型的锁命名为Insert Intention Locks,官方的类型名称为:LOCK_INSERT_INTENTION,我们称为插入意向锁 。插入意向锁是一种 Gap锁,不是意向锁,在insert操作时产生。
插入意向锁是在插入一条记录行前,由
INSERT 操作产生的一种间隙锁
。该锁用以表示插入意向,当多个事务在同一区间(gap)插入位置不同的多条数据时,事务之间不需要互相等待。假设存在两条值分别为4和7的记录,两个不同的事务分别试图插入值为5和6的两条记录,每个事务在获取插入行上独占的(排他)锁前,都会获取(4,7)之间的间隙锁,但是因为数据行之间并不冲突,所以两个事务之间并不会产生冲突(阻塞等待)。总结来说,插入意向锁的特性可以分成两部分:

  1. 插入意向锁是一种特殊的间隙锁– 间隙锁可以锁定开区间内的部分记录,
  2. 插入意向锁之间互不排斥,所以即使多个事务在同一区间插入多条记录,只要记录本身(主键、唯一索引)不冲突,那么事务之间就不会出现冲突等待。

注意,虽然插入意向锁中含有意向锁三个字,但是它并不属于意向锁而属于间隙锁,因为意向锁是表锁而插入意向锁是行锁

在这里插入图片描述

从图中可以看到,由于T1持有gap锁,所以T2和T3需要生成一个插入意向锁的锁结构并且处于等待状态。当T1提交后会把它获取到的锁都释放掉,这样T2和T3就能获取到对应的插入意向锁了(本质上就是把插入意向锁对应锁结构的is_waiting属性改为false)T2和T3之间也并不会相互阻塞,它们可以同时获取到id值为8的插入意向锁,然后执行插入操作。事实上插入意向锁并不会阻止别的事务继续获取该记录上任何类型的锁。

举例:插入意向锁的使用场景模拟

场景:事务1开启间隙锁,事务2和事务3分别插入数据,在事务1不提交之前,事务2和事务3都是进入阻塞状态,直到事务1提交,事务2和事务3同时成功

事务1:开启间隙锁

mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> select * from user where id = 6 for update;
Empty set (0.00 sec)

事务2:插入数据

mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> insert into user (id,name) values (7,"李四2323");
Query OK, 1 row affected (34.91 sec)

事务3:插入数据

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into user (id,name) values (6,"李四2323");
Query OK, 1 row affected (6.35 sec)

事务1未提交,事务2和事务3进入阻塞状态,事务1提交,事务2和事务3则新增成功
MySQL | 表锁——排他锁、共享锁、意向锁、元数据锁

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 基于OpenHarmony的实验室智能化改造
  • Node.js 中托管本地图片文件
  • 代码随想录算法训练营day43|动态规划part10
  • 4 C 语言变量、printf 基本输出、scanf 基本输入、关键字、标识符及其命名规则
  • day36——homework
  • cocosUI多分辨率适配
  • 初心 | AIGC时代下的思想蜕变
  • Java 阿里云视频直播开发流程
  • C语言中10个字符串函数详解
  • 你对开源项目有什么期待?
  • 阿里云-java调用短信服务,第三方接口的开启(傻瓜式教程)
  • SSLVPN对比IPSECVPN安全设备的起源、发展、以及目前行业使用场景
  • KEEPALIVED是什么?以及实现各功能的配置实验
  • 一键换肤(Echarts 自定义主题)
  • 89. UE5 RPG 实现伤害 冷却 消耗技能描述
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • 【划重点】MySQL技术内幕:InnoDB存储引擎
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • Cookie 在前端中的实践
  • FastReport在线报表设计器工作原理
  • mysql中InnoDB引擎中页的概念
  • select2 取值 遍历 设置默认值
  • SSH 免密登录
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • Windows Containers 大冒险: 容器网络
  • 服务器从安装到部署全过程(二)
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • 自制字幕遮挡器
  • # MySQL server 层和存储引擎层是怎么交互数据的?
  • #nginx配置案例
  • #pragma once与条件编译
  • (1)无线电失控保护(二)
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (2)MFC+openGL单文档框架glFrame
  • (C语言)二分查找 超详细
  • (Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)
  • (计算机网络)物理层
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (十)DDRC架构组成、效率Efficiency及功能实现
  • (一)appium-desktop定位元素原理
  • ******IT公司面试题汇总+优秀技术博客汇总
  • .md即markdown文件的基本常用编写语法
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NET Core 和 .NET Framework 中的 MEF2
  • .NET Framework .NET Core与 .NET 的区别
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • .NET教程 - 字符串 编码 正则表达式(String Encoding Regular Express)
  • .NET开源纪元:穿越封闭的迷雾,拥抱开放的星辰
  • @param注解什么意思_9000字,通俗易懂的讲解下Java注解
  • @Service注解让spring找到你的Service bean
  • [000-01-018].第3节:Linux环境下ElasticSearch环境搭建
  • [001-03-007].第07节:Redis中的事务
  • [4.9福建四校联考]
  • [C#]winform使用onnxruntime部署LYT-Net轻量级低光图像增强算法