最近在开发的时候,在mysql Innodb 引擎下,一条记录记录也能引起锁的事件。

场景描述

在项目压测的是,突然发现有类似以下的异常发生:

com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
at sun.reflect.GeneratedConstructorAccessor247.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
at com.mysql.jdbc.Util.getInstance(Util.java:381)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1045)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3491)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3423)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1936)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2060)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2542)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1734)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2019)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1937)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1922)

但代码里面明明只锁了一条记录,不存在多个资源的时候,死锁又是怎么来的呢。

例子

首先创建实验数据:

DROP TABLE IF EXISTS `account_info`;
CREATE TABLE `account_info` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`account` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`current_amt` decimal(18,2) DEFAULT NULL,
PRIMARY KEY (`uid`),
UNIQUE KEY `idx_account` (`account`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; INSERT INTO `account_info` VALUES (1, 'tester', 1.00);

这里的是 uid 是主键, account 是唯一索引。

时间 Transaction1 Transaction2
T1 set autocommit=false;
begin;
T2 select * from account_info where uid = 1 for update;
T3 set autocommit=false;
begin;
T4 update account_info set current_amt = current_amt +1 where account = 'tester';
T5 update account_info set current_amt = current_amt +1 where account = 'tester';

在执行到T5的时候,mysql 返回了一下的异常:

1213 - Deadlock found when trying to get lock; try restarting transaction, Time: 0.000000s

What? 死锁? 锁一条记录也会死锁?

原理分析

在 mysql 的 innodb 引擎下,行级锁并不是直接锁记录,而是锁索引,如果一条 sql 语句用到了主键索引,mysql 会锁住主键索引;如果一条语句操作了非主键索引,mysql 会先锁住该非主键索引,再锁住主键索引。

例子

  • 新开一个session,执行一下语句
set autocommit=false;
begin;
select * from account_info where uid = 1 for update;
  • 然后再打开另外一个session,执行查看 innodb 事务
select * from information_schema.INNODB_TRX;

会出现一下内容摘要,其中 trx_rows_locked 内容是 1

trx_id trx_state trx_tables_locked trx_rows_locked trx_rows_modified trx_isolation_level
5831745 RUNNING 1 1 0 REPEATABLE READ
  • 把前面的 session 关闭后,再重新打开一个session执行一下语句
set autocommit=false;
begin;
select * from account_info where account = 'tester' for update;
  • 然后再打开另外一个session,执行查看 innodb 事务
select * from information_schema.INNODB_TRX;

会出现一下内容摘要,其中 trx_rows_locked 内容是 2

trx_id trx_state trx_tables_locked trx_rows_locked trx_rows_modified trx_isolation_level
5831748 RUNNING 1 2 0 REPEATABLE READ

会看到上面的例子,用主键索引加锁的时候,只会出现一把锁,若用非主键索引的时候,就出现了两把锁,这也是单条记录造成了死锁的原因。

微信关注我,发现更多java领域知识

mysql单记录也能造成的死锁的更多相关文章

  1. MySQL单表最大记录数不能超过多少?

    MySQL单表最大记录数不能超过多少? 很多人困惑这个问题.其实,MySQL本身并没有对单表最大记录数进行限制,这个数值取决于你的操作系统对单个文件的限制本身. 从性能角度来讲,MySQL单表数据不要 ...

  2. MySQL单表多字段模糊查询

    今天工作时遇到一个功能问题:就是输入关键字搜索的字段不只一个字段,比如 我输入: 超天才 ,需要检索出 包含这个关键字的 name . company.job等多个字段.在网上查询了一会就找到了答案. ...

  3. MySQL行(记录)的详细操作一 介绍 二 插入数据INSERT 三 更新数据UPDATE 四 删除数据DELETE 五 查询数据SELECT 六 权限管理

    MySQL行(记录)的详细操作 阅读目录 一 介绍 二 插入数据INSERT 三 更新数据UPDATE 四 删除数据DELETE 五 查询数据SELECT 六 权限管理 一 介绍 MySQL数据操作: ...

  4. python 3 mysql 单表查询

    python 3 mysql 单表查询 1.准备表 company.employee 员工id id int 姓名 emp_name varchar 性别 sex enum 年龄 age int 入职 ...

  5. day 37 MySQL行(记录)的详细操作

    MySQL行(记录)的详细操作   阅读目录 一 介绍 二 插入数据INSERT 三 更新数据UPDATE 四 删除数据DELETE 五 查询数据SELECT 六 权限管理 一 介绍 MySQL数据操 ...

  6. MySQL单表数据不超过500万:是经验数值,还是黄金铁律?

    今天,探讨一个有趣的话题:MySQL 单表数据达到多少时才需要考虑分库分表?有人说 2000 万行,也有人说 500 万行.那么,你觉得这个数值多少才合适呢? 曾经在中国互联网技术圈广为流传着这么一个 ...

  7. MySQL单表数据不要超过500万行:是经验数值,还是黄金铁律?

    本文阅读时间大约3分钟. 梁桂钊 | 作者 今天,探讨一个有趣的话题:MySQL 单表数据达到多少时才需要考虑分库分表?有人说 2000 万行,也有人说 500 万行.那么,你觉得这个数值多少才合适呢 ...

  8. mySQL单表限制大小

    MySQL单表大小的限制在目前的技术环境中,由所在主机的OS上面的文件系统来界定而不是由MySQL数据库本身来决定了. 在老版本的MySQL 3.22中,MySQL单表大小为4GB,当时的MySQL的 ...

  9. 一文了解MySQL性能测试及调优中的死锁处理方法,你还看不明白?

    一文了解MySQL性能测试及调优中的死锁处理方法,你还看不明白? 以下从死锁检测.死锁避免.死锁解决3个方面来探讨如何对MySQL死锁问题进行性能调优. 死锁检测 通过SQL语句查询锁表相关信息: ( ...

随机推荐

  1. SpringBoot应用操作Rabbitmq(fanout广播高级操作)

    一.广播模式fanout.不需要指定路由key. 注:与topic和direct区别是:fanout广播模式会两个队列同时发送相同的消息,并非由交换器转发到某一个队列 二.实战(广播模式) 1.引入m ...

  2. c语言实现乘法口诀表

    利用c语言实现乘法口诀表的两种输出: #include <stdio.h> int main(int argc, char *argv[]) { ; ;a<;a++){//输出1-9 ...

  3. XEP-0198:流管理

    ------------恢复内容开始------------ 原文来自:https://xmpp.org/extensions/xep-0198.html,只翻译了技术方面的内容. 摘要:这个规范定义 ...

  4. 即将进行论文答辩的我发现MyEclipse 2016 激活过期害得我又一次把 MyEclipse 2016 给重新激活注册,详细的图文解说激活过程

    背景: 在家美滋滋的上着网课享受着因为疫情带来的平静,没想到随着微信.钉钉铃声响起打破了我半年以来的平静的生活:通知我们过完劳动节要进行答辩,由于我的答辩项目是由 MyEclipse 这个工具编写的我 ...

  5. 利用POI工具读取word文档并将数据存储到sqlserver数据库中

    今天实现了利用POI工具读取word文档,并将数据存储到sql数据库中,代码如下: package word; import java.io.File; import java.io.FileInpu ...

  6. JavaSE——装饰设计模式+简单加密解密工程

    2019独角兽企业重金招聘Python工程师标准>>> 声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权:凯哥学堂旨在促进VIP学员互相学习的基础 ...

  7. P1459 三值的排序 Sorting a Three-Valued

    题目描述 排序是一种很频繁的计算任务.现在考虑最多只有三值的排序问题.一个实际的例子是,当我们给某项竞赛的优胜者按金银铜牌排序的时候.在这个任务中可能的值只有三种1,2和3.我们用交换的方法把他排成升 ...

  8. E - Help Jimmy POJ - 1661 dp

    E - Help Jimmy POJ - 1661 这个题目本身不是很难,但是可以更加优化这个写法. 开始是n*n #include <cstdio> #include <cstri ...

  9. EditPlus编辑java代码 常规配置

  10. Mysql数据库多表联查

    内连接:查询的是多个表的交集 外连接:查询的是一张表的全部数据和另一张表满足要求的数据 student数据库表 grade数据库表 内连接: 隐式内连接 SELECT s.id, s. NAME, s ...