learn and grow up

mysql诡异死锁系列二之高并发删除下的死锁二

字数统计: 478阅读时长: 2 min
2020/12/01 Share

写在前面

​ 前面分析的明白了,那现在我们就来复现下上文中的错误

正文

  1. 废话不多说,直接上代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    @Test
    public void webPushTest() throws InterruptedException {

    AtomicBoolean atomicBoolean=new AtomicBoolean(true);

    for(int i=0;;){
    appPushMessageClientMappingMapper.saveTest();


    Thread t1=new Thread(new Runnable() {
    @Override
    public void run() {
    try{

    int i = clientManager.deleteClientIds(new ArrayList<String>(){{add("773479997251391488_iot_websocket_778553066747727872");}});
    log.info("int {}",i);
    }catch (Exception e){
    log.error("e",e);
    atomicBoolean.getAndSet(false);
    }
    }
    },i+"t1");
    Thread t2=new Thread(new Runnable() {
    @Override
    public void run() {
    try{

    int i = clientManager.deleteClientIds(new ArrayList<String>(){{add("773479997251391488_iot_websocket_778553066747727872");}});
    log.info("int {}",i);
    }catch (Exception e){
    log.error("e",e);
    atomicBoolean.getAndSet(false);
    }
    }
    },i+"t2");
    Thread t3=new Thread(new Runnable() {
    @Override
    public void run() {
    try{

    int i = clientManager.deleteClientIds(new ArrayList<String>(){{add("773479997251391488_iot_websocket_778553066747727872");}});
    log.info("int {}",i);
    }catch (Exception e){
    log.error("e",e);
    atomicBoolean.getAndSet(false);
    }
    }
    },i+"t3");

    t1.start();
    t2.start();
    t3.start();


    t1.join();
    t2.join();
    t3.join();

    if(!atomicBoolean.get()){
    break;
    }

    Thread.sleep(500L);
    i++;
    }



    }

    代码之所以这么写,是因为根据前面分析,此类死锁,产生的几个前提:

    • Delete操作,针对的是唯一索引上的等值查询的删除;(范围下的删除,也会产生死锁,但是死锁的场景,跟本文分析的场景,有所不同)

    • 至少有3个(或以上)的并发删除操作;

    • 并发删除操作,有可能删除到同一条记录,并且保证删除的记录一定存在;

    • 事务的隔离级别设置为Repeatable Read,同时未设置innodb_locks_unsafe_for_binlog参数(此参数默认为FALSE);(Read Committed隔离级别,由于不会加Gap锁,不会有next key,因此也不会产生死锁)

    • 使用的是InnoDB存储引擎;(废话!MyISAM引擎根本就没有行锁)

      实际测试结果表示在以上条件下运行,在运行一定的时间后肯定会出现上述问题,如下:

      死锁

      死锁

      问题复现~

CATALOG
  1. 1. 写在前面
  2. 2. 正文