高并发下数据库插入操作死锁问题

1. 问题:

项目中出现如下报错:

org.springframework.dao.DeadlockLoserDataAccessException: com.xxxMapper.insert (batch index #1) failed. 
Cause: java.sql.BatchUpdateException: Deadlock found when trying to get lock; try restarting transaction; 
Deadlock found when trying to get lock; try restarting transaction; 
nested exception is java.sql.BatchUpdateException: Deadlock found when trying to get lock; try restarting transactionjava.sql.BatchUpdateException:Deadlock found when trying to get lock;try restarting transaction

此场景在多线程批量同时对两张表进入插入操作,一张A表,一张B表,A表是原始数据表,B表是处理数据表,插入B表时,每批次插入数据量为50,产生死锁导致数据不一致问题。A表与B表数据不一致,并且比对时以A表为准,所以B表的数据永远有问题,不会及时更新。

2. 问题原因:

2.1 事务的四大特性(ACID)

  1. 原子性(atomicity):
    事务是一个原子操作,要么全部执行成功,要么全部执行失败。 事务的原子性确保一组逻辑操作,要么全部完成,要么完全不起作用。

  2. 一致性(consistency):
    执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的。

  3. 隔离性(isolation):
    事务的隔离性是指在并发执行的多个事务中,每个事务的执行互不影响,每个事务都有自己独立的空间进行操作。事务隔离级别越高,数据冲突的可能性就越小,但并发性能也会受到一定的影响。

  4. 持久性(durability):
    一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障,应用重启,也不应该对其有任何影响。

2.2 数据库的四种事务隔离级别

数据库事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。在事务的并发操作中可能会出现脏读,不可重复读,幻读。

  1. 读未提交(Read uncommitted):
    一个事务读到了另一个事务还没有提交的数据。
    但是会产生脏读。
  2. 读已提交(Read committed):
    一个事务要等另一个事务提交后才能读取数据。
    但是会产生不可重复读。不可重复读说的是某一条数据发生了改变。
  3. 可重复读(Repeatable read)
    同一事务下,事务在执行期间,多次读取同一数据时,能够保证读取到的数据是一致的。
    但是会产生幻读。幻读与不可重复读不同,它说的是多出来了数据。
  4. 串行化(Serializable)
    它是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率最低,比较耗费数据库性能,一般不推荐使用。
隔离级别脏读不可重复读幻读
读未提交可能出现可能出现可能出现
读已提交不会出现可能出现可能出现
可重复读不会出现不会出现可能出现
串行化不会出现不会出现不会出现

2.3 产生死锁的原因

2.3.1 日志查看

发生死锁时,查看最后一次死锁的日志。

show engine innodb status;
2.3.2 数据库的锁

数据库InnoDB 中的行锁有多种类型:

  1. 记录锁(RECORD LOCK)
    对索引记录加锁。

  2. 间隙锁(GAP LOCK,也叫范围锁)
    对索引记录的所在间隙加锁,在 RR 隔离级别下,用于解决幻读的问题(实际上在 RC 隔离级别下,也会产生间隙锁)。

S 间隙锁和 X 间隙锁是兼容的,不同的事务可以在同一个间隙加锁。

  1. NEXT-KEY 锁
    相当于 RECORD LOCK + GAP LOCK。

  2. 插入意向锁(INSERT INTENTION LOCK)
    GAP 锁的一种,在执行 INSERT 前,如果待插入记录的下一条记录上被加了 GAP 锁,则 INSERT 语句被阻塞,且生成一个插入意向锁。

仅会被 GAP 锁阻塞。

  1. 隐式锁
    新插入的记录,不生成锁结构,但由于事务 ID 的存在,相当于加了隐式锁;别的事务要对这条记录加锁前,先帮助其生成一个锁结构,然后再进入等待状态。
2.3.2 产生死锁的原因

INSERT 语句加锁类型

  1. 被 GAP 锁阻塞时,生成一个插入意向锁。
  2. 遇到重复键冲突时
    主键冲突,产生 S 型记录锁(RR 和 RR 隔离级别,实际上在 INSERT 阶段时还是会请求 GAP 锁)。
    唯一键冲突,产生 S 型 NEXT-KEY 锁(RR 和 RR 隔离级别)。
    注意:INSERT 语句正常执行时,不会生成锁结构。

INSERT … ON DUPLICATE KEY UPDATE 和 REPLACE 如果遇到重复键冲突

如果是主键冲突,加 X 型记录锁(RR 和 RR 隔离级别,实际上在 INSERT 阶段时还是会请求 GAP 锁)。
如果是唯一键冲突,加 X 型 NEXT-KEY 锁(RR 和 RR 隔离级别)。

2.3.3 情况1

INSERT语句
T1 时刻
session1 插入记录成功,此时对应的索引记录被隐式锁保护,未生成锁结构。

T2 时刻
session2 插入记录检测到插入值和 session1 唯一键冲突。

session2 帮助 session1 对 a=35 的记录产生了一个显式的锁结构。
session2 自身产生 S 型的 NEXT-KEY LOCK,请求范围为 (30,35],但是其只能获取到 (30,35) 的 GAP LOCK,而被 session1 的 a=35 的记录锁阻塞。

T3 时刻
session1 插入 a=33,被 session2 (30,35)间隙锁阻塞。

闭环锁等待,死锁条件达成:

session1 持有 session2 需要的 a=35 记录锁,且请求 session2 持有的 (30,35) GAP 锁。
session2 持有 session1 需要的 (30,35) GAP 锁,且请求 session1 持有的记录锁。

此情况的解决方案:
在一个事务中的 INSERT 按照主键或唯一键的顺序增序插入,即 session1 可以先插入 a=33 的记录,再插入 a=35 的记录,可一定程度避免受到 GAP 锁的影响。
一个事务中只插入一行记录,且尽快提交。

2.3.4 情况2

REPLACE语句,可参考此处

此情况的解决方案:
在唯一键冲突时,INSERT、INSERT … ON DUPLICATE KEY UPDATE 的加锁范围要比 REPLACE 加锁范围小,在该场景下,可使用 INSERT … ON DUPLICATE KEY UPDATE 代替 REPLACE 来避免死锁,有兴趣的可以自己测试下。

3. 解决方案:

注意点:
在 REPEATABLE-READ 级别,事务持有的 每个锁 在整个事务期间一直被持有。
在 READ-COMMITED 级别,事务里面特定语句结束之后,不匹配该sql语句扫描条件的锁,会被释放。

建议:

  1. 以固定的顺序访问表和行
  2. 大事务拆分成小事务。大事务更倾向于死锁,如果业务允许,将大事务拆小。
  3. 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
  4. 降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
  5. 为表添加合理的索引(如果不走索引,将会为表的每一行记录添加上锁,増加死锁的概率)。
  6. INSERT … ON DUPLICATE KEY UPDATE 比 REPLACE 产生死锁的几率小且更安全高效。

以上的小建议都在一定程度上减少和避免的死锁的发生,但是还可能会发生,因此,在业务端做好容错处理也是重要的。比如说本篇博客中的A表与B表可以校验两者的数据一致性,因为高并发会时时刷新数据,因此即使是死锁造成的短暂的数据不一致,在下一次刷新时,也会刷新为最新的数据。

参考:
官方文档:
https://dev.mysql.com/doc/refman/8.0/en/innodb-locks-set.html
https://blog.csdn.net/qq271859852/article/details/79284740
https://www.panziye.com/java/4659.html
https://baijiahao.baidu.com/s?id=1781188447015451234&wfr=spider&for=pc
有用:
https://zhuanlan.zhihu.com/p/624468049
https://blog.csdn.net/songjiweiliu/article/details/131136171
https://zhuanlan.zhihu.com/p/92959304
有些东西:
https://zhuanlan.zhihu.com/p/528365818
https://zhuanlan.zhihu.com/p/654416860
https://blog.csdn.net/minghao0508/article/details/129093202
https://juejin.cn/post/6844903854165721101#heading-0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/582086.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Transformer实现的一个Demo

RT,直接上代码,可以跑通: #encoding:utf-8 import torch import torch.nn as nn import numpy as np import math class Config(object): def __init__(self): self.vocab_size 6 self.d_model 512 self.n_heads 4 assert self.d_model…

UI自动化Selenium 元素定位之Xpath

一、元素定位方式 selenium中定位元素,通常有几种方式: 1、通过id定位:By.ID 2、通过Name定位:By.Name 3、通过元素其他属性定位,如class、type、text文本。。。。。。等等,如果要用属性定位那就需要使…

图论 经典例题

1 拓扑排序 对有向图的节点排序,使得对于每一条有向边 U-->V U都出现在V之前 *有环无法拓扑排序 indegree[], nxs[];//前者表示节点 i 的入度,后者表示节点 i 指向的节点 queue [] for i in range(n):if indege[i] 0: queue.add(i)// 入度为0的节…

虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类的指针在释放时无法调用到子类的析构代码 解决方式:将父类中的析构代码函数改为虚析构或者纯虚析构 虚析构和纯虚析构共性: 可以解决父类指针释放子类对象 都需要有具体的函数…

[SWPUCTF 2021 新生赛]finalrce

[SWPUCTF 2021 新生赛]finalrce wp 注&#xff1a;本文参考了 NSSCTF Leaderchen 师傅的题解&#xff0c;并修补了其中些许不足。 此外&#xff0c;参考了 命令执行(RCE)面对各种过滤&#xff0c;骚姿势绕过总结 题目代码&#xff1a; <?php highlight_file(__FILE__); …

【算法练习】leetcode链表算法题合集

链表总结 增加表头元素倒数节点&#xff0c;使用快慢指针环形链表&#xff08;快慢指针&#xff09;合并有序链表&#xff0c;归并排序LRU缓存 算法题 删除链表元素 删除链表中的节点 LeetCode237. 删除链表中的节点 复制后一个节点的值&#xff0c;删除后面的节点&#x…

verilog 通过DPI-C调用C 流水灯模拟

verilog 通过DPI-C调用C简单示例&#xff0c; verillator模拟 ledloop.v module ledloop(input wire clk,output wire[3:0] LED );reg[31:0] cnt 32h00000000;always (posedge clk)cnt < cnt 1;assign LED 4b0001 << cnt[21:20]; endmodule电脑模拟较慢&#xff…

如何解决服务器CA证书过期的问题

一、问题的提出 最近在学习VPS&#xff0c;在Linux系统里给服务器安装某项服务时&#xff0c;在服务的log里看到下面的错误信息&#xff1a; failed to verify certificate: x509: certificate has expired or is not yet valid: current time 2023-12-25T04:42:38-05:00 is a…

java球队信息管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Web球队信息管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5…

深度学习之RNN

1.循环神经网络 在时间t的时候&#xff0c;对于单个神经元来讲它的输出y(t)如下 wx是对于输入x的权重&#xff0c;wy是对于上一时刻输出的权重 所以循环神经网络有两个权重。 如果有很多这样的神经元并排在一起 则在t时刻的输出y为 这时输入输出都是向量 2.记忆单元 由于循…

java系列-CountDownLatch

CountDownLatch 不是一种锁&#xff0c;而是一种同步工具类&#xff0c;用于协调多个线程之间的操作。它并不是像 ReentrantLock 或 synchronized 关键字那样实现了锁定机制&#xff0c;而是通过一个计数器来实现线程的等待和通知。 具体来说&#xff0c;CountDownLatch 维护了…

车队试验的远程实时显示方案

风丘科技推出的数据远程实时显示方案更好地满足了客户对于试验车队远程实时监控的需求&#xff0c;并真正实现了试验车队的远程管理。随着新的数据记录仪软件IPEmotion RT和相应的跨平台显示解决方案的引入&#xff0c;让我们的客户端不仅可在线访问记录器系统状态&#xff0c;…

LaTeX 不同章的图片放在不同的文件夹

需求&#xff1a;在写长文档的时候&#xff0c;比如学位论文&#xff0c;每一章都有好几张图片&#xff0c;整个文档一共几十张甚至上百张图片&#xff0c;如果不分开放&#xff0c;想修改某一张图片的时候&#xff0c;找起来比较困难。所以&#xff0c;想把每一章的图片单独放…

git unable to create temporary file: No space left on device(git报错)

1.问题 1.1 vscode中npm run serve跑项目的时候&#xff0c;进度达到95%的时候一直卡着无进度&#xff1b; 1.2 git命令提交代码报错&#xff1b; 2.具体解决 这个错误通常表示你的磁盘空间已经满了&#xff0c;导致 Git 无法在临时目录中创建文件。2.1 清理磁盘空间&#xf…

LeetCode75| 区间集合

目录 435 无重叠区间 452 用最少的箭引爆气球 435 无重叠区间 class Solution { public:static bool cmp(vector<int>&a,vector<int>&b){return a[0] < b[0];}int eraseOverlapIntervals(vector<vector<int>>& intervals) {int res …

低代码平台在金融银行中的应用场景

随着数字化转型的推进&#xff0c;商业银行越来越重视技术在业务发展中的作用。在这个背景下&#xff0c;白码低代码平台作为一种新型的开发方式&#xff0c;正逐渐受到广大商业银行的关注和应用。白码低代码平台能够快速构建各类应用程序&#xff0c;提高开发效率&#xff0c;…

WebGoat 指定端口号

文章目录 新版本的 WebGoat旧版本 WebGoat 新版本的 WebGoat 使用 WEBGOAT_PORT 指定 WebGoat 的端口号 使用 WEBWOLF_PORT 指定 WebWolf 的端口号 java -DWEBGOAT_PORT8081 -jar webgoat-2023.8.jar java -DWEBGOAT_PORT8081 -DWEBWOLF_PORT9091 -jar webgoat-2023.8.jar W…

跨境电商引流真的很难吗?了解一下这些技巧!

随着全球电商市场的不断扩大&#xff0c;越来越多的企业开始涉足跨境电商领域&#xff0c;然而&#xff0c;与国内电商相比&#xff0c;跨境电商面临着诸多挑战&#xff0c;其中最大的难题之一就是如何有效地吸引潜在客户。 很多卖家觉得跨境电商引流非常困难&#xff0c;但实…

解析数据时代----驱动变革与重塑商业的力量

随着科技的飞速发展&#xff0c;我们正处在一个信息爆炸的时代。数据&#xff0c;作为这个时代的核心要素&#xff0c;已经渗透到各个领域&#xff0c;深刻影响着我们的生活、工作和商业模式。本文将深入解析数据时代的特点、影响以及如何应对数据带来的挑战&#xff0c;以适应…

springBoot整合redis做缓存

一、Redis介绍 Redis是当前比较热门的NOSQL系统之一&#xff0c;它是一个开源的使用ANSI c语言编写的key-value存储系统&#xff08;区别于MySQL的二维表格的形式存储。&#xff09;。和Memcache类似&#xff0c;但很大程度补偿了Memcache的不足。和Memcache一样&#xff0c;R…