这几天,在查看文章时,发现了一个Mysql并发的问题,在一开始仅仅凭借眼睛去查看时,并未发现问题及解决方法,于是我们对其进行了具体实际操作和测试:

(想自学习编程的小伙伴请搜索圈T社区,更多行业相关资讯更有行业相关免费视频教程。完全免费哦!)

一、问题

一个事务内:insert记录后根据字段p来update这条记录,然而当出现并发操作的时候,update处会发生dead lock问题,把update改为id,就没事了。

同一个表,高并发事务,事务内先插入一条记录,再更新这条记录:
(1)如果更新的是唯一索引,有异常;
(2)如果更新的是自增主键,就没有异常;
画外音:先不要被“dead lock”描述所迷惑,是死锁问题,阻塞问题,还是其他异常,还另说。

二、测试问题及复现

2.1 数据准备

create table t (id int(20) primary key AUTO_INCREMENT,cell varchar(20) unique)engine=innodb;

新建表: (1)存储引擎是innodb,MySQL版本是5.6; (2)id字段,自增主键; (3)cell字段,唯一索引;

start transaction;
insert into t(cell) values(11111111111);
insert into t(cell) values(22222222222);
insert into t(cell) values(33333333333);
commit;

插入一些测试数据。

2.2 session参数设置

设置事务隔离级别为RR(repeatable read)

--设置手动提交
--设置事务隔离级别为RR
set session autocommit=0;
set session transaction isolation level repeatable read;

2.3 模拟并发

多个终端session模拟并发事务

start TRANSACTION;
INSERT INTO t(cell) VALUES(44444444);
UPDATE t set cell = 123 WHERE cell = 44444444 ;
ROLLBACK;
start TRANSACTION;
INSERT INTO t(cell) VALUES(5555555);
UPDATE t set cell= 456 WHERE cell = 5555555 ;
ROLLBACK;

在Navicat中开启两个窗口

  1. 窗口A,先启动事务,并插入记录;
  2. 窗口B,再启动事务,也插入记录;
  3. 窗口A,修改插入的记录;
  4. 窗口B,也修改插入的记录;

2.4 结果

奇怪的出现了!
  • 当运行到事务1的update时,发生了等待!
  • 当运行到事务2的update时,发生了死锁,自动回滚了

三、查询问题

按道理,插入不冲突的记录,然后修改这条记录,行锁不应该冲突呀?唯一索引,主键索引怎么会有差异呢?是否有关?是死锁,还是其他原因?

3.1 根据show engine innodb status查询

百思不得其解,那就先看看innodb status里都有什么吧,复制粘贴下来后查看:

可见Transaction1与Transaction2 同时锁住了同一部分,而且是locak_mode X rec bur not gap Record lock

这就很奇怪了,又不是间隙锁引起的死锁,第一次update为什么会等待呢,第二次update为啥会死锁呢?

不懂,就换个地方看看

3.2 查看innodb_locks表

通过查看information_schema库中inndb_locks表,可看到,确实事务1和事务2,同时锁住了一片数据区域,导致了数据的等待、死锁,但是原因呢?

于是再次换方法查看:

3.3 explain/desc sql语句

咦!发现了重大问题
为什么这里rows居然是6!
我update为什么会扫了全表??
我是加了索引的啊

找到问题了:update没走索引,而是扫了全表!

四、解决问题

既然找到问题了,就看看如何解决,为什么update没有走索引呢?
那我们回头再看看两个update语句

UPDATE t set cell = 123 WHERE cell = 44444444 ;
UPDATE t set cell= 456 WHERE cell = 5555555 ;

看着是没啥问题呀?

寻寻腻腻,冷冷清清,凄凄惨惨戚戚,终于,在查看表时,发现了问题:

回头看建表语句/表结构

create table t (id int(20) primary key AUTO_INCREMENT,cell varchar(20) unique)engine=innodb;

cell字段数据类型是varchar类型的,而我们的update写的是cell = 444444;

并未对数据加引号!而导致了update没走索引,扫了全表

于是,我们再从头看看这个过程:

在事务隔离级别为RR(Repeat Read)下
事务1的insert产生了一个插入意向锁,事务2的insert也产生了一个插入意向锁(不会被互相锁住,因为数据行并不冲突)
此时事务1再进行update语句,因未走索引,导致扫全表,而在扫到事务2插入那条数据时,行锁与插入意向锁冲突了,导致事务1需要等待事务2释放插入意向锁而进行等待。
事务2在进行update时,也同样需要扫全表,但是全表都被事务1的update锁住了,事务2需要等待 等待事务2释放插入意向锁的 事务1 的行锁 释放,因此发生了死锁

那解决方法就很简单了,将语句改为:

UPDATE t set cell = 123 WHERE cell = "44444444" ;
UPDATE t set cell= 456 WHERE cell = "5555555" ;

即可解决死锁/等待问题

五、引伸问题

5.1 RC与RR的比较

5.1.1 表中包含历史数据的测试

其实在进行测试时,也曾经怀疑过是不是因为RR的问题,改成RC试试呢?

--将事务隔离级别改为RC
SET TRANSACTION ISOLATION LEVEL REPEATABLE COMMITTED;

修改后,对其进行相同的操作:

发现:事务1insert,事务2insert,事务1的update生效,事务2的update发生了等待

根据上文中我们找到的问题,对其进行分析:

  • 事务1在进行update时,也是扫了全表,但是因为RC没有间隙锁,没有插入意向锁,因此事务1的update不会进行等待
  • 事务2在进行update时,需要等待事务1的update提交释放锁,因此发生了等待

得到结论:

RC下不存在间隙锁

5.1.2 表中不包含历史数据的测试(表为空)

对于RC和RR的比较,我们对表中数据采取了删数据的方法继续进行测试:

truncate table

继续对表进行相同操作,结果:

  • RR下仍然是事务1等待,事务2死锁
  • RC下却是事务1与事务2都正常,未发生等待

究其原因,RR下,事务1插入的数据,事务2能看到,因此在RR下,即使数据清空,事务1仍然锁住了事务2插入的数据。
而在RC下,事务1插入的数据事务2看不到,事务2插入的数据事务1看不到,他们各自仅仅锁住了自己插入的数据,因此能执行成功。

5.1.3 结论

抛开可重复读和读已提交他们在同一个事务中多次读可读出的东西外,此次发现了他们其他的不同:

  • RC中不存在间隙锁、同样不存在属于间隙锁的插入意向锁
  • RR下,事务1插入的数据事务2 能看到,RC下事务1插入的数据事务2看不到

5.2 Mysql中间隙锁与插入意向锁

在此次过程中,我们发现了: 行锁、间隙锁、插入意向锁。其中因不同的行为产生了不同的锁,而其意义、用处也是不同的:

5.2.1 间隙锁(Gap Locks)
  1. 区间锁, 仅仅锁住一个索引区间(开区间,不包括双端端点)。
  2. 在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身。比如在 1、2、3中,间隙锁的可能值有 (∞, 1),(1, 2),(2, ∞)。
  3. 间隙锁可用于防止幻读,保证索引间的不会被插入数据
5.2.2 插入意向锁(Insert Intention Locks)
  1. 插入意向锁是一种Gap锁,不是意向锁,在insert操作时产生。
  2. 在多事务同时写入不同数据至同一索引间隙的时候,并不需要等待其他事务完成,不会发生锁等待。
  3. 假设有一个记录索引包含键值4和7,不同的事务分别插入5和6,每个事务都会产生一个加在4-7之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突。
  4. 插入意向锁不会阻止任何锁,对于插入的记录会持有一个记录锁。

5.2.3 锁的选择

在我们处理sql语句去执行时,不同的语句会选择不同的锁:

  • 如果更新条件没有走索引,例如执行”update test set name=“hello” where name=“world”;” ,此时会进行全表扫描,扫表的时候,要阻止其他任何的更新操作,所以上升为表锁。

  • 如果更新条件为索引字段,但是并非唯一索引(包括主键索引),例如执行“update test set name=“hello” where code=9;” 那么此时更新会使用Next-Key Lock。使用Next-Key Lock的原因:

  1. 首先要保证在符合条件的记录上加上排他锁,会锁定当前非唯一索引和对应的主键索引的值;

  2. 还要保证锁定的区间不能插入新的数据。

  3. 如果更新条件为唯一索引,则使用Record Lock(记录锁)。

记一次Mysql并发死锁,引出的问题及讨论相关推荐

  1. mysql并发死锁问题解决

    一般根据多条件过滤后更新update在高并发的时候会导致死锁,进而事务失败.解决办法就是为表增加主键,先查询出主键,再按主键更新,避免死锁. 这是因为mysql行级锁并不是直接锁记录,而是锁索引,如果 ...

  2. mysql 并发死锁_Mysql并发时经典常见的死锁原因

    Mysql并发时经典常见的死锁原因 更新时间:2017-06-07 00:17:21 1256次阅读 评论 0 1.mysql都有什么锁 MySQL有三种锁的级别:页级.表级.行级. 表级锁:开销小, ...

  3. 记一次mysql数据库死锁实验

    概述 之前接触到的数据库死锁,很多都是批量更新时加锁顺序不一致而导致的死锁,但是上周却遇到了一个很难理解的死锁.借着这个机会又重新学习了一下mysql的死锁知识以及常见的死锁场景.今天不介绍死锁的基本 ...

  4. MySQL事务原理分析(ACID特性、隔离级别、锁、MVCC、并发读异常、并发死锁以及如何避免死锁)

    MySQL事务原理分析(ACID特性.隔离级别.锁.MVCC.并发读异常.并发死锁以及如何避免死锁) 一.事务 目的 组成 特征 事务空间语句 二.ACID特性 原子性(A) 隔离性(I) 持久性(d ...

  5. 关于MySQL的死锁问题

    本文来说下关于MySQL的死锁问题 文章目录 什么是死锁 InnoDB锁类型 间隙锁( gap lock ) next-key lock 意向锁( Intention lock ) 插入意向锁( In ...

  6. mysql 代替不明_所有的死锁,都是不明就里的循环。又一必须升级MySQL的死锁问题!!!...

    原标题:所有的死锁,都是不明就里的循环.又一必须升级MySQL的死锁问题!!! 去年年底到今年年初,线上发生了3次MySQL数据库hang住的情况.在内部,我们将其称为半死不活的场景.具体的表现表现如 ...

  7. MySQL 遇到的死锁问_一个罕见的MySQL redo死锁问题排查及解决过程

    原标题:一个罕见的MySQL redo死锁问题排查及解决过程 作者:张青林,腾讯云布道师.MySQL架构师,隶属腾讯TEG-基础架构部-CDB内核开发团队,专注于MySQL内核研发&相关架构工 ...

  8. mysql数据库死锁几种情况

    mysql数据库死锁的产生原因及解决办法 数据库和操作系统一样,是一个多用户使用的共享资源.当多个用户并发地存取数据 时,在数据库中就会产生多个事务同时存取同一数据的情况.若对并发操作不加控制就可能会 ...

  9. mysql 数据库 死锁_Mysql数据库出现死锁的情况(一)

    在临近上线之前,我们系统做了一次压力测试,发现有一个接口在高并发情况下会出现一个死锁的情况..首先申明-不是我写的,我只是帮忙排查下. 随着对Mysql锁的深入了解,于是就准备写几篇文章来记录下Mys ...

最新文章

  1. 王维嘉:神经网络的本质是在数据里面提取相关性
  2. Linux Mint 13 root登录
  3. vue里实现同步执行方法_vue中的watch方法 实时同步存储数据
  4. 信息学奥赛一本通 1168:大整数加法 | OpenJudge NOI 1.6 10:大整数加法
  5. vscode制表位_vscode 常用配置
  6. 一个不会言谈的程序猿,惊奇发现自己能力的多么的优秀
  7. Android 使用SwipeBackLayout实现滑动返回上一级页面——实战来袭
  8. matlab如何制作莫兰散点图,求大神指点绘制空间内散点图的包络面,,,散点程序如下...
  9. ruijie交换机lacp动态_vmware esxi 做链路聚合LACP踩坑
  10. 年底互联网寒冬,裁员直线上升
  11. MediaWiki搭建指导
  12. 编程疑难杂症の无法剔除的神秘重复记录
  13. python中的图形界面设计_python图形化界面设计(tkinter)一全面介绍
  14. GPT时代,最令人担心的其实是“塔斯马尼亚效应”
  15. Numpy 获取数组的行和列
  16. Nginx服务器搭建
  17. html 3d坐标,3d transform的坐标空间及位置_html/css_WEB-ITnose
  18. Xshell远程登录中方向键及删除键出现乱码问题
  19. 001 书法学习---永
  20. 使用虚拟登录页面jsp,跳过登录页,直接访问主页面

热门文章

  1. 推荐一个可以倍速播放HTML5视频的脚本
  2. ctfshow MengXIn 下(pearcmd.php妙用条件竞争简单密码简单misc)
  3. 期刊缩写合辑【JCR+ISO】
  4. CUDA 学习(CUDA实战 第四章)
  5. python tushare获取股票数据_python调用tushare获取沪股通、深股通成份股数据
  6. WPF制作贪吃蛇小游戏
  7. 想知道如何将PDF合并成一个文件?一分钟教会你
  8. ToG产品_产品白皮书框架_2019_003
  9. HDU-ACM程序设计——BFS(宽度优先搜索)
  10. ios安卓模拟器_IOS全球首款手游模拟器,牛逼哄哄但没有卵用!