首页 > 数据库 >GreatSQL 死锁案例分析

GreatSQL 死锁案例分析

时间:2024-04-19 10:15:09浏览次数:23  
标签:RECORD LOCK GRANTED GreatSQL GAP 案例 死锁 test c1

1.背景概述

客户业务发生死锁的报错,根据业务程序日志及业务流程,发现造成死锁的原因是:事务1 delete + insert ,事务2 delete + insert 2个事务交替执行导致的死锁;由于GAP锁阻塞了插入意向锁,并且当delete的数据存在时死锁不会发生,当delete的数据不存在时,会发生死锁。

2.问题复现

本次测试基于 GreatSQL-8.0.32-24,隔离级别为 RR

2.1 创建测试表

greatsql> create database test;

greatsql> create table test(c1 int unique key, c2 int, c3 int, c4 int);

greatsql> insert into test values (1,1,1,1),(3,3,3,3),(5,5,5,5),(9,9,9,9);

greatsql> select * from test;
+------+------+------+------+
| c1  | c2  | c3  | c4  |
+------+------+------+------+
|   1 |   1 |   1 |   1 |
|   3 |   3 |   3 |   3 |
|   5 |   5 |   5 |   5 |
|   9 |   9 |   9 |   9 |
+------+------+------+------+
4 rows in set (0.01 sec)

2.2 事务执行顺序

按以下事务执行顺序,如果要删除的数据存在,则不会发生死锁;如果要删除的数据不存,并且要删除的数据在同一个GAP锁的区间内则会发生死锁;

时间 事务1 事务2
T1 BEGIN; BEGIN;
T2 delete from test where c1=?;
T3 delete from test where c1=?;
T4 insert into test value(?,?,?,?);
T5 insert into test value(?,?,?,?);

2.3 当delete的数据存在时

事务1:delete

greatsql> begin;
Query OK, 0 rows affected (0.00 sec)

greatsql> delete from test where c1=3;
Query OK, 1 row affected (0.00 sec)

查看锁信息:

greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;

+-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+
| THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME    | LOCK_TYPE | LOCK_MODE   | LOCK_STATUS | LOCK_DATA     |
+-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+
|     59 | test      | test     | NULL     | TABLE   | IX       | GRANTED   | NULL        |
|     59 | test      | test     | c1       | RECORD   | X,REC_NOT_GAP | GRANTED   | 3, 0x000000000201 |
|     59 | test      | test     | GEN_CLUST_INDEX | RECORD   | X,REC_NOT_GAP | GRANTED   | 0x000000000201   |
+-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+
3 rows in set (0.00 sec)

此时事务1给 3, 0x000000000201 这条数据加了 记录锁 X,REC_NOT_GAP

事务2:delete

greatsql> begin;
Query OK, 0 rows affected (0.00 sec)

greatsql> delete from test where c1=5;
Query OK, 1 row affected (0.00 sec)

查看锁信息:

greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;

+-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+
| THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME    | LOCK_TYPE | LOCK_MODE   | LOCK_STATUS | LOCK_DATA     |
+-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+
|     57 | test      | test     | NULL       | TABLE   | IX       | GRANTED   | NULL        |
|     57 | test      | test     | c1        | RECORD   | X,REC_NOT_GAP | GRANTED   | 5, 0x000000000202 |
|     57 | test      | test     | GEN_CLUST_INDEX | RECORD   | X,REC_NOT_GAP | GRANTED   | 0x000000000202   |
|     59 | test      | test     | NULL       | TABLE   | IX       | GRANTED   | NULL        |
|     59 | test      | test     | c1        | RECORD   | X,REC_NOT_GAP | GRANTED   | 3, 0x000000000201 |
|     59 | test      | test     | GEN_CLUST_INDEX | RECORD   | X,REC_NOT_GAP | GRANTED   | 0x000000000201   |
+-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+
6 rows in set (0.00 sec)

此时事务2给 5, 0x000000000202 这条数据加了 记录锁 X,REC_NOT_GAP

事务1:insert

greatsql> insert into test value(3,3,3,3);
Query OK, 1 row affected (0.00 sec)

查看锁信息:

greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;

+-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+

| THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME    | LOCK_TYPE | LOCK_MODE   | LOCK_STATUS | LOCK_DATA     |

+-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+

|     57 | test      | test     | NULL       | TABLE   | IX       | GRANTED   | NULL        |

|     57 | test      | test     | c1        | RECORD   | X,REC_NOT_GAP | GRANTED   | 5, 0x000000000202 |

|     57 | test      | test     | GEN_CLUST_INDEX | RECORD   | X,REC_NOT_GAP | GRANTED   | 0x000000000202   |

|     59 | test      | test     | NULL       | TABLE   | IX       | GRANTED   | NULL        |

|     59 | test      | test     | c1        | RECORD   | X,REC_NOT_GAP | GRANTED   | 3, 0x000000000201 |

|     59 | test      | test     | GEN_CLUST_INDEX | RECORD   | X,REC_NOT_GAP | GRANTED   | 0x000000000201   |

|     59 | test      | test     | c1        | RECORD   | S,GAP     | GRANTED   | 3, 0x000000000201 |

|     59 | test      | test     | c1        | RECORD   | S,GAP     | GRANTED   | 5, 0x000000000202 |

|     59 | test      | test     | c1        | RECORD   | S,GAP     | GRANTED   | 3, 0x000000000206 |

+-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+
9 rows in set (0.00 sec)

此时事务1给 被delete删除的数据 3, 0x000000000201 ,插入的数据 3, 0x000000000202,以及相邻的下一条数据 5, 0x000000000202 加了 间隙锁 S,GAP

事务2:insert

greatsql> insert into test value(5,5,5,5);
Query OK, 1 row affected (0.01 sec)

查看锁信息:

greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;

+-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+
| THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME    | LOCK_TYPE | LOCK_MODE   | LOCK_STATUS | LOCK_DATA     |
+-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+
|     57 | test      | test     | NULL       | TABLE   | IX       | GRANTED   | NULL        |
|     57 | test      | test     | c1        | RECORD   | X,REC_NOT_GAP | GRANTED   | 5, 0x000000000202 |
|     57 | test      | test     | GEN_CLUST_INDEX | RECORD   | X,REC_NOT_GAP | GRANTED   | 0x000000000202   |
|     57 | test      | test     | c1        | RECORD   | S,GAP     | GRANTED   | 5, 0x000000000202 |
|     57 | test      | test     | c1        | RECORD   | S,GAP     | GRANTED   | 9, 0x000000000203 |
|     57 | test      | test     | c1        | RECORD   | S,GAP     | GRANTED   | 5, 0x000000000207 |
|     59 | test      | test     | NULL       | TABLE   | IX       | GRANTED   | NULL        |
|     59 | test      | test     | c1        | RECORD   | X,REC_NOT_GAP | GRANTED   | 3, 0x000000000201 |
|     59 | test      | test     | GEN_CLUST_INDEX | RECORD   | X,REC_NOT_GAP | GRANTED   | 0x000000000201   |
|     59 | test      | test     | c1        | RECORD   | S,GAP     | GRANTED   | 3, 0x000000000201 |
|     59 | test      | test     | c1        | RECORD   | S,GAP     | GRANTED   | 5, 0x000000000202 |
|     59 | test      | test     | c1        | RECORD   | S,GAP     | GRANTED   | 3, 0x000000000206 |
+-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+
12 rows in set (0.00 sec)

此时事务2给 被delete删除的数据 5, 0x000000000202 ,插入的数据 5, 0x000000000207,以及相邻的下一条数据 9, 0x000000000203 加了 间隙锁 S,GAP

由于GAP锁之间是相互兼容的,所以没有发生锁等待及死锁,此时事务1,事务2都执行完成,可以正常提交。

2.4 当delete的数据不存在时

事务1:delete

greatsql> begin;
Query OK, 0 rows affected (0.01 sec)

greatsql> delete from test where c1=6;
Query OK, 0 rows affected (0.00 sec)

查看锁信息:

greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;

+-----------+---------------+-------------+------------+-----------+-----------+-------------+-------------------+
| THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA     |
+-----------+---------------+-------------+------------+-----------+-----------+-------------+-------------------+
|     61 | test      | test     | NULL    | TABLE   | IX     | GRANTED   | NULL        |
|     61 | test      | test     | c1     | RECORD   | X,GAP   | GRANTED   | 9, 0x000000000203 |
+-----------+---------------+-------------+------------+-----------+-----------+-------------+-------------------+
2 rows in set (0.00 sec)

此时事务1给 9, 0x000000000203 这条数据加了 间隙锁 X,GAP

事务2:delete

greatsql> begin;
Query OK, 0 rows affected (0.00 sec)
 
greatsql> delete from test where c1=7;
Query OK, 0 rows affected (0.00 sec)

查看锁信息:

greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;
+-----------+---------------+-------------+------------+-----------+-----------+-------------+-------------------+
| THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA     |
+-----------+---------------+-------------+------------+-----------+-----------+-------------+-------------------+
|     62 | test      | test     | NULL    | TABLE   | IX     | GRANTED   | NULL        |
|     62 | test      | test     | c1     | RECORD   | X,GAP   | GRANTED   | 9, 0x000000000203 |
|     61 | test      | test     | NULL    | TABLE   | IX     | GRANTED   | NULL        |
|     61 | test      | test     | c1     | RECORD   | X,GAP   | GRANTED   | 9, 0x000000000203 |
+-----------+---------------+-------------+------------+-----------+-----------+-------------+-------------------+
4 rows in set (0.00 sec)

此时事务2给 9, 0x000000000203 这条数据加了 间隙锁 X,GAP 间隙锁可以相互兼容,因此没有报错

事务1:insert

greatsql> insert into test value(6,6,6,6);
---hang住,处于锁等待

查看锁信息:

greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;

+-----------+---------------+-------------+------------+-----------+------------------------+-------------+-------------------+
| THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE        | LOCK_STATUS | LOCK_DATA     |
+-----------+---------------+-------------+------------+-----------+------------------------+-------------+-------------------+
|     62 | test      | test     | NULL    | TABLE   | IX           | GRANTED   | NULL        |
|     62 | test      | test     | c1     | RECORD   | X,GAP          | GRANTED   | 9, 0x000000000203 |
|     61 | test      | test     | NULL    | TABLE   | IX           | GRANTED   | NULL        |
|     61 | test      | test     | c1     | RECORD   | X,GAP          | GRANTED   | 9, 0x000000000203 |
|     61 | test      | test     | c1     | RECORD   | X,GAP,INSERT_INTENTION | WAITING   | 9, 0x000000000203 |
+-----------+---------------+-------------+------------+-----------+------------------------+-------------+-------------------+
5 rows in set (0.00 sec)

此时事务1,要申请给 9, 0x000000000203 这条数据加 间隙锁,插入意向锁 X,GAP,INSERT_INTENTION;由于事务2已经加了 间隙锁 X,GAP 间隙锁与插入意向锁并不兼容,因此事务1的insert处于锁等待状态

事务2:insert

greatsql> insert into test value(7,7,7,7);
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

查看锁信息:

greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;

+-----------+---------------+-------------+-----------------+-----------+------------------------+-------------+------------------------+
| THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME    | LOCK_TYPE | LOCK_MODE        | LOCK_STATUS | LOCK_DATA        |
+-----------+---------------+-------------+-----------------+-----------+------------------------+-------------+------------------------+
|     61 | test      | test     | NULL       | TABLE   | IX           | GRANTED   | NULL          |
|     61 | test      | test     | c1        | RECORD   | X,GAP          | GRANTED   | 9, 0x000000000203    |
|     61 | test      | test     | c1        | RECORD   | X,GAP          | GRANTED   | 6, 0x000000000213    |
|     61 | test      | test     | GEN_CLUST_INDEX | RECORD   | X            | GRANTED   | supremum pseudo-record |
|     61 | test      | test     | GEN_CLUST_INDEX | RECORD   | X,GAP          | GRANTED   | 0x000000000213     |
|     61 | test      | test     | c1        | RECORD   | X,GAP,INSERT_INTENTION | GRANTED   | 9, 0x000000000203    |
+-----------+---------------+-------------+-----------------+-----------+------------------------+-------------+------------------------+
6 rows in set (0.01 sec)

事务2回滚,只有事务1的加锁信息。

由于此时事务2,要申请给 9, 0x000000000203 这条数据加 间隙锁,插入意向锁由于事务1已经加了 间隙锁 X,GAP 间隙锁与插入意向锁并不兼容,因此事务2的insert处于锁等待状态,2个事务相互等待锁导致死锁,此时事务2回滚。

3.总结

此次死锁的发生主要是GAP 锁 和 插入意向锁的冲突,建议让业务修改一下逻辑,先判断数据是否存在 select 一下,存在的话 delete 后在 insert ; 不存在的话直接 insert 不用delete了。


Enjoy GreatSQL

标签:RECORD,LOCK,GRANTED,GreatSQL,GAP,案例,死锁,test,c1
From: https://www.cnblogs.com/greatsql/p/18145183

相关文章

  • java桥接模式案例
    桥接模式(BridgePattern)是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立变化。在桥接模式中,抽象类和它的实现类之间使用组合/聚合关系,而不是继承关系。这样,客户端代码可以针对抽象编程,而实现细节则可以在运行时动态地指定。场景:假设我们要设计一个图形绘制系统......
  • Python案例:输出公元后到目前为止全部回文日期
    一、回文日期一个日期,如果顺读和倒读都一样,那么就称之为回文日期,比如今天:20211202,就是一个神奇的回文日期。二、提出任务输出公元后的全部回文日期要求每行输出五个回文日期统计总共有多少个回文日期三、完成任务(一)涉及知识点1、time模块time模块有两个常用函数time()......
  • 死锁
    多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致俩个或者多个线程都在等待对方释放资源。packagesyn;publicclassDeadLock{publicstaticvoidmain(String[]args){Makeups1=newMakeup(0,"小明");Makeups2=new......
  • NLP自然语言处理—主题模型LDA案例:挖掘人民网留言板文本数据|附代码数据
    全文链接:http://tecdat.cn/?p=2155最近我们被客户要求撰写关于NLP自然语言处理的研究报告,包括一些图形和统计输出。随着网民规模的不断扩大,互联网不仅是传统媒体和生活方式的补充,也是民意凸显的地带。领导干部参与网络问政的制度化正在成为一种发展趋势,这种趋势与互联网发展的时......
  • NLP自然语言处理—主题模型LDA回归可视化案例:挖掘智能门锁电商评价数据
    全文链接:http://tecdat.cn/?p=2175早在1995年比尔·盖茨就在《未来之路》里说过:未来没有配套智能家居的房子,就是毛坯房。现在人们生活越来越便捷,人们也更加倾向于智能化家居,当你还在纠结“人工智能”安利值不值得吃,最近不少朋友家里又出现智能门锁,相比传统门锁来说,究竟能有多智能......
  • iNeuOS工业互联网操作系统,民爆远程运维平台案例
    iNeuOS工业互联网操作系统,民爆远程运维平台案例目      录1.     概述...22.     iNeuOS在民爆生产厂区和北京运维中心配置...31.1          生产厂区配置...31.2          运维中心配置...7 1.  概述     针对......
  • 第十六章——处理锁、阻塞和死锁(3)——使用SQLServer Profiler侦测死锁
    前言:作为DBA,可能经常会遇到有同事或者客户反映经常发生死锁,影响了系统的使用。此时,你需要尽快侦测和处理这类问题。死锁是当两个或者以上的事务互相阻塞引起的。在这种情况下两个事务会无限期地等待对方释放资源以便操作。下面是死锁的示意图: 本文将使用SQLServerProfi......
  • java多线程 读取变量同步安全的案例
    本次介绍,我使用的是synchronized同步代码块的关键字来读取list,在写java多线程时,一定要注意synchronized关键字的有效范围。ps:如果synchronized关键字的代码块范围太大,可能会导致优先获取到cpu资源的第一个线程在满足条件的情况下一直无法跳循环,从而使得其他线程无法给获......
  • GreatSQL优化技巧:半连接(semijoin)优化
    何为半连接?半连接是在GreatSQL内部采用的一种执行子查询的方式,semijoin不是语法关键字,不能像使用innerjoin、leftjoin、rightjoin这种语法关键字一样提供给用户来编写SQL语句。两个表t1表和t2表进行半连接的含义是:对于t1表的某条记录来说,我们只关心在t2表中是否存在与之匹配......
  • Oracle+RAC静默安装系列(基于RHEL9/国产/麒麟/华为欧拉的生产案例)
    由风哥发布的 Oracle+RAC静默安装系列(基于RHEL9/国产/麒麟/华为欧拉的生产案例)系列,适合运维人员/数据库/开发人员,可以用于业务生产环境。为满足想快速安装布署Oracle数据库的学员,风哥特别设计的一套比较全面的全命令行静默安装配置数据库课程,本系列共7套课程,内容如下:1)全......