问题究竟在哪里,一次诡异的数据库“死锁”?

    作者:课课家教育更新于: 2019-07-09 11:22:47

    一般认为,一个数据结构是由数据元素依据某种逻辑联系组织起来的。对数据元素间逻辑关系的描述称为数据的逻辑结构;数据必须在计算机内存储,数据的存储结构是数据结构的实现形式,是其在计算机内的表示;此外讨论一个数据结构必须同时讨论在该类数据上执行的运算才有意义。一个逻辑数据结构可以有多种存储结构,且各种存储结构影响数据处理的效率。

    前段时间,和一个朋友讨论了一个“疑似”数据库死锁的问题,最后进行试验与排查,找到了问题所在。

    程序死锁的问题,很难调试,看进程堆栈,看各个线程与锁的情况,对照代码进行排查。

    数据库死锁的问题,更难,看不了数据库堆栈,也看不了数据库线程与锁,更难以对照代码排查。

    问题究竟在哪里,一次诡异的数据库死锁_数据库_数据结构_数据分析_课课家

    前段时间,和一个朋友讨论了一个“疑似”数据库死锁的问题,最后进行试验与排查,找到了问题所在。

    场景如下:

    同一个表,高并发事务,事务内先插入一条记录,再更新这条记录:

    • 如果更新的是唯一索引,有异常;
    • 如果更新的是自增主键,就没有异常;

    画外音:先不要被“dead lock”描述所迷惑,是死锁问题,阻塞问题,还是其他异常,还另说。

    而且,据朋友所述,还能够复现:

    • 开启事务;
    • 插入记录;
    • sleep 5秒;
    • 修改被插入的记录;

    在并发时稳定复现。

    根据朋友的描述,在线下开了多个MySQL客户端进行了并发模式测试,结果还挺出乎意料的。

    第一步:数据准备

    1. create table t ( 
    2. id int(20) primary key AUTO_INCREMENT, 
    3. cell varchar(20) unique 
    4. )engine=innodb

    新建表:

    • 存储引擎是innodb,MySQL版本是5.6;
    • id字段,自增主键;
    • cell字段,唯一索引;
    1. start transaction; 
    2. insert into t(cell) values(11111111111); 
    3. insert into t(cell) values(22222222222); 
    4. insert into t(cell) values(33333333333); 
    5. commit; 

    插入一些测试数据。

    第二步:session参数设置

    事务的隔离级别,事务的自动提交等参数设置不当,都会对实验的结果产生影响,询问了朋友,事务的隔离级别是RR(repeatable read)。

    1. set session autocommit=0
    2. set session transaction isolation level repeatable read; 

    每一个session启动后:

    • 关闭自动提交;
    • 把事务隔离级别设为RR;

    1. show session variables like "autocommit"; 
    2. show session variables like "tx_isolation"; 

    不放心的话,可以用上面两个语句查询确认。

    第三步:多个终端session模拟并发事务

    CAD1b938f6c8e8f.jpg" target="_blank">

    如上图,用SecureCRT开启两个窗口:

    • 窗口A,先启动事务,并插入记录;
    • 窗口B,再启动事务,也插入记录;
    • 窗口A,修改插入的记录;
    • 窗口B,也修改插入的记录;

    奇怪的现象发生了,如果并发事务的update语句:

    • 更新条件是cell,就会发生异常;
    • 更新条件是id,就一切正常;

    按道理,插入不冲突的记录,然后修改这条记录,行锁不应该冲突呀?唯一索引,主键索引怎么会有差异呢?是否有关?是死锁,还是其他原因?

     选择了数据结构,算法也随之确定,是数据而不是算法是系统构造的关键因素。这种洞见导致了许多种软件设计方法和程序设计语言的出现,面向对象的程序设计语言就是其中之一。

课课家教育

未登录