概述

前面两篇文章介绍了MySQL的全局锁和表级锁,今天就介绍一下MySQL的行锁。

MySQL的行锁是各个引擎内部实现的,不是所有的引擎支持行锁,例如MyISAM就不支持行锁。

不支持行锁就意味着在并发操作时,就要使用表锁,在任意时刻都只能有一个更新操作在执行,这样会影响业务的并发性。这也是为什么MyISAM会被InnoDB取代的原因之一。

行锁是锁里最小粒度的锁,InnoDB引擎里的行锁的实现算法有三种:

Record Lock:行锁,锁住记录本身

Gap Lock:间隙锁,锁住某个范围,但不包括记录本身

Next-Key Lock:Record Lock + Gap Lock,既锁范围,又锁记录

InnoDB是使用Next-Key Lock来解决幻读问题的。

什么是幻读?

我们看一下这个例子,有一个表 t,插入部分数据。

CREATE TABLE `t` (

`id` int(11) NOT NULL,

`c` int(11) DEFAULT NULL,

`d` int(11) DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `c` (`c`)

) ENGINE=InnoDB;

insert into t values(0,0,0),(5,5,5),

(10,10,10),(15,15,15),(20,20,20),(25,25,25);

图1 假设只在id=5这一行加行锁

有三个会话并发执行,Session A在T1,T3,T5时刻分别查询同一个语句,出现不同的结果。其中Q3读到的id=1这一行的现象,被称为幻读。

幻读,指同一个事务中,两次相同的查询操作,得到的结果行数不一样。

这里要对“幻读”做两点说明:

在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。因此幻读在“当前读”下才会出现。

上面的Session B的修改结果,被Session A之后的select语句用“当前读”看到了,不能称为幻读。幻读仅专指“新插入的行”。

根据数据可见性规则分析,这三个查询都加了for update,都是“当前读”,符合数据可见性规则。

这么看来,好像没什么问题,是不是真的没有问题呢?

不,这里还真就有问题。

幻读有什么问题?

语义上不一致

Session A在T1时刻就声明了,“我要把所有d=5的行锁住,不准别的事务进行读写操作”。而实际上,这个语义被破坏了。

上面的例子可能还看不太出来,我们给Session B和Session C分别加两个语句,再看看会出现什么现象。

图2 假设只在id=5这一行加行锁--语义被破坏

Session B的第二条语句update t set c = 5 where id=0,语义是“我要把id=0、d=5的这一行的c的值改成了5”。

由于在T1时刻,Session A还只是给t=5这一行加了行锁,并没有给id=0这一行加锁。因此Session B在T2时刻,是可以执行这条语句的。

同理,Session C对id=1这行的修改,一样是破坏了Q1的加锁声明。

数据上不一致

其次是造成数据上不一致。锁的设计就是为了保证数据一致性的,这里的一致性除了内部数据在此刻的一致性外,还包含数据和日志在逻辑上的一致性。

图 3 假设只在id=5这一行加行锁--数据一致性问题

我们来分析一下图3执行完成后,数据库的数据是什么:

经过T1时刻,id=5这一行变成 (5,5,100),当然这个结果最终是在T6时刻正式提交的

经过T2时刻,id=0这一行变成(0,5,5);

经过T4时刻,表里面多了一行(1,5,5);

我们再来看看binlog的内容:

// session B

update t set d=5 where id=0;

update t set c=5 where id=0;

// session C

insert into t values(1,1,5);

update t set c=5 where id=1;

update t set d=100 where d=5;

按照这个语句序列,这三行的结果变成:(0,5,100),(1,5,100),(5,5,100)。

也就是说id=0和id=1这两行,发生了数据不一致。这个问题很严重,是不行的。

那究竟这个数据不一致是怎样引入的呢?

图 4 假设扫描到的行都被加上了行锁

假设我们对扫描到的行都加上行锁,来看看图4执行后会出现什么现象。

经过T1时刻,id=5这一行变成 (5,5,100),当然这个结果最终是在T6时刻正式提交的

经过T2时刻,Session B被阻塞,等到T6时刻Session A释放锁才能执行;

经过T4时刻,表里面多了一行(1,5,5);

经过T6时刻,id=1这一行变成(1,5,100);

id=1这一行还是出现数据不一致的问题。即使把所有的记录都加上锁,还是阻止不了新插入的记录。

如何解决幻读?

我们现在知道产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。因此,为了解决幻读问题,InnoDB引入了间隙锁(Gap Lock)。

前面介绍过,间隙锁,锁住某个范围,但不包括记录本身。比如前面说到的表t,初始化有6条记录,这就产生了7个间隙。

图 5 表t主键索引上的行锁和间隙锁

当你执行select * from t where d=5 for update的时候,就不止是给数据库中6个记录加了行锁,还同时加了7个间隙锁。这样就确保了无法再插入新的记录。

也就是说这时候,在一行行扫描的过程中,不仅给行加上行锁,还给行两边的空隙也加上间隙锁。

我们回到上面的图4,再来看看加上间隙锁后,执行的效果如何。

经过T1时刻,id=5这一行变成 (5,5,100),当然这个结果最终是在T6时刻正式提交的。因为select * from t where d=6 for update,对6个记录加了行锁,同时加了7个间隙锁。

经过T2时刻,Session B被阻塞,因为id=0这一行被锁;

经过T4时刻,Session C被阻塞,因为主键索引上加了间隙锁(0,5),所以id=1这个值无法被插入;

Session B和Session C都要等待Session A释放锁后才能继续执行,这样就解决了幻读的问题。

行锁保证更新行,间隙锁保证插入行,而行锁+间隙锁=Next-Key Lock,也就是本文开头说到的,InnoDB是通过Next-Key Lock来解决幻读问题的。

但是间隙锁的引入,可能会导致同样的语句锁住更大的范围,这会影响并发度的。比如上面的select * from t where d=5 for update,相当于加了表锁。

参考资料

mysql行锁怎么读_MySQL锁(三)行锁:幻读是什么?如何解决幻读?相关推荐

  1. MySQL面试知识点追命连环问(三)锁机制、日志备份及分表策略

    ​上次我们讨论了MySQL的事务索引,SQL优化和处理器. MySQL面试知识点追命连环问(二)事务.索引及SQL优化 这次我们继续来追命连环问关于MySQL锁机制,日志备份和扩展性等相关的内容. 锁 ...

  2. 小米id锁状态查询_Mysql中的三类锁,你知道吗?

    点击上方"码农沉思录",选择"设为星标" 优质文章,及时送达 导读 正所谓有人(锁)的地方就有江湖(事务),人在江湖飘,怎能一无所知? 今天不聊江湖,来细说一下 ...

  3. mysql电脑缺少dll文件下载_mysql.dll文件下载,金山毒霸dll修复工具帮您解决文件丢失导致“mysql.dll找不到”的系统问题...

    我该如何安装从金山毒霸下载的DLL文件? 一: 1.从金山毒霸下载压缩文件. 2.将DLL文件解压到电脑上的某个地方. 3.把该文件跟要求使用它的程序放在同一路径上.注意32位程序需要使用32位的DL ...

  4. mysql锁表更新_Mysql InnoDB 数据更新导致锁表

    一.数据表结构 CREATE TABLE `jx_attach` ( `attach_id` int(11) NOT NULL AUTO_INCREMENT, `feed_id` int(11) DE ...

  5. mysql几种安装方法_mysql的三种安装方式(详细)

    安装MySQL的方式常见的有三种: rpm包形式 通用二进制形式 源码编译 1,rpm包形式 (1) 操作系统发行商提供的 (2) MySQL官方提供的(版本更新,修复了更多常见BUG)www.mys ...

  6. 用命令行连接mysql文件时出现错误_MySQL使用cmd命令窗口安装错误信息提示的解决方法...

    作者:二龙_01 转载自https://blog.csdn.net/ 近期学习使用mysql数据库遇到了一些安装上的问题,自己也上网百度了各种解决方法,其中有适合自己的,也发现了一些人云亦云的解决方法 ...

  7. mysql脚本执行中文乱码_MySQL从命令行导入SQL脚本时出现中文乱码的解决方法

    本文实例讲述了MySQL从命令行导入SQL脚本时出现中文乱码的解决方法.分享给大家供大家参考,具体如下: 在图形界面管理工具 MySql Query Browser中打开脚本(脚本包括建库.建表.添加 ...

  8. mysql 设置主键命令_mysql用命令行如何设置主键

    mysql用命令行设置主键的方法:首先进入到mysql命令行工具,并通过use关键字进行到目标数据库里:然后通过命令ADD PRIMARY KEY来添加主键,并回车即可. mysql用命令行设置主键的 ...

  9. mysql把相同id的多行合并到一行_mysql中将多行数据合并成一行数据

    一个字段可能对应多条数据,用mysql实现将多行数据合并成一行数据 例如:一个活动id(activeId)对应多个模块名(modelName),按照一般的sql语句: 1 SELECT am.acti ...

  10. mysql自定义函数的分号_MySQL基础(三)—函数、自定义函数

    上一篇 MySQL基础(二)-操作表记录 这一篇是对函数的笔记,其中操作的数据库在上一篇文章中有代码,可以去看一下. 1.函数 1.1:函数的分类 字符函数 数值运算符与函数 比较运算符与函数 日期时 ...

最新文章

  1. js 数组过滤_JS之 开发技巧
  2. 设计把所有的奇数移动到所有偶数前面的算法
  3. 大学c语言下上机考试题,计算机考试二级C语言上机试题下[5]
  4. java类验证和装载顺序_Java类加载机制实现流程及原理详解
  5. MySQL——JSON_REPLACE()函数修改JSON属性值
  6. JavaScript把客户端时间转换为北京时间
  7. R(1):中文乱码解决方案
  8. 一行代码打印python之禅
  9. 按键精灵定位坐标循环_[按键精灵教程]带你了解多点找色、多点比色
  10. VSCode 如何修改字体
  11. vscode返回快捷键
  12. 花费9.9元使用使用中国知网
  13. java swing tooltips_【Unity笔记】提示框ToolTips大小自适应,及其闪烁的问题
  14. 电脑软件测试英雄联盟,揭秘英雄联盟的自动化测试
  15. 电脑qq游戏程序更改计算机,电脑怎么把qq游戏快捷到桌面
  16. 矩阵并行加速之NENO与SSE
  17. 程序龙的密码(递归算法)
  18. Matplotlib绘图笔记
  19. java web 找回密码_java web实现 忘记密码(找回密码)功能及代码
  20. 人体神经系统分布图图片,神经系统分布图片高清

热门文章

  1. [iOS-UI]点击清空按钮,却会有提交的感觉
  2. stored to '*' during its initialization is never read
  3. Pulseaudio调用alsalib open()流程(十五)
  4. Android之制作img镜像文件系统
  5. P2P打洞原理(二十二)
  6. Linux printf()占位符
  7. Android7.0以上增加(/system/lib/libtest.so)系统库文件
  8. Mac串口工具(COMTool)
  9. Mac/Linux/Win使用scrcpy投屏
  10. Android NDK开发从0到1