最近在开发的时候,在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. jdbc批量插入数据

    //插入很多书(批量插入用法) public void insertBooks(List<Book> book) {   final List<Book> tempBook=b ...

  2. if __name__ == '__main__'到底是什么?

    引子 要搞清楚这个问题,可以先听一个故事~~~ 像我们做事一样,都需要一个起始点,终点存在与否无关紧要.编程也是一样,任何程序都有一个入口,在所谓的静态编译语言中,如Java的入口是一个名字叫做Mai ...

  3. 已有项目接入git远程仓库

    1.项目根目录初始化git仓库 git init 2.将本地项目与远程仓库关联(首先得在远程创建一个代码仓库) git remote add origin 远程仓库地址 诺,仓库地址就是这个玩意 3. ...

  4. vscode快捷替换json格式

    i=json from=AUTO to=AUTO smartresult=dict client=fanyideskweb salt=1537359261311 sign=06b7d1398ebdc6 ...

  5. 以内存级速度实现存储?XPoint正是我们的计划

    随着计算能力虚拟化技术的普及,存储机制在速度上远逊于内存这一劣势开始变得愈发凸显. 这一巨大的访问速度鸿沟一直是各项存储技术想要解决的核心难题:纸带.磁带.磁盘驱动器乃至闪存记忆体等等,而如今最新一代 ...

  6. JSP、ASP、PHP Web应用程序怎么这么多P!

    之前我们说完了计算机网络应用程序的两种结构:C/S,B/S(传送门)今天我们详细说一说B/S开发中的这么多P是干什么的. 1.什么是Web应用程序 一个Web应用程序是由完成特定任务的各种Web组件( ...

  7. Git 上传本地项目到远程仓库 (工具篇)

    前言:前面一开始写了一篇通过命令来操作本地项目上传远程仓库的文章,后来发现此方式没有那么灵活.故跟开发同事请教了下,知道了通过工具来操作更方便.所以写了这篇文章来分享&记录. 前提条件:本地安 ...

  8. libevent(六)http server

    客户端: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signa ...

  9. 【面试题】String类、包装类的不可变性

    不可变类的意思是创建该类的实例后,该实例的实例变量是不可改变的.Java提供的8个包装类和String类都是不可变类.因此String和8个包装类都具有不可变性. 就拿String类来说,通过阅读St ...

  10. 前端——Vue-cli 通过UI页面创建项目

    在使用该教程创建项目时请先安装vue ui,具体安装方法请百度 1.打开CMD,输入vue ui 2.点击创建按钮,选择项目目录 3.填写项目名 4.配置项目 选择项目所需要的模块