暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL 高频面试题解析 第07期:有哪些死锁场景

悦专栏 2020-11-11
4587

作者简介

马听,多年 DBA 实战经验,对 MySQL、 Redis、ClickHouse 等数据库有一定了解,专栏《一线数据库工程师带你深入理解 MySQL》、《Redis 运维实战》作者。


这一节内容就来聊聊高频面试题:MySQL 有哪些死锁场景?

首先一起来复习一下死锁的概念:死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。

下面我们通过几个实验,来验证几种死锁场景。

1 环境准备

    use martin;
    drop table if exists dl;
    CREATE TABLE `dl` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `a` int(11) NOT NULL,
    `b` int(11) NOT NULL,
    `c` int(11) NOT NULL,
    PRIMARY KEY (`id`),
    KEY `idx_c` (`a`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


    CREATE TABLE `dl_insert` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `a` int(11) NOT NULL,
    `b` int(11) NOT NULL,
    `c` int(11) NOT NULL,  
    PRIMARY KEY (`id`),
    unique key `uniq_a` (`a`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


    insert into dl(a,b,c) values (1,1,1),(2,2,2);
    drop table if exists dl_1;
    create table dl_1  like dl;
    insert into dl_1 select * from dl;


    2 同一张表下的死锁

    session1

    session2

    begin;

    begin;

    select * from dl where a=1 for update;…1 row in set (0.00 sec)

    select * from dl where a=2 for update;…1 row in set (0.00 sec)

    select * from dl where a=2 for update;/* SQL1 */(等待)


    (session2 提示死锁回滚后,SQL1 成功返回结构)

    select * from dl where a=1 for update;ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

    commit;

    commit;

    session1 在等待 session2 释放 a=2 的行锁,而 session2 在等待 session1 释放 a=1 的行锁。两个 session 互相等待对方释放资源,就进入了死锁状态。


    3 不同表下的死锁

    session1

    session2

    begin;

    begin;

    select * from dl where a=1 for update; … 1 row in set (0.00 sec)

    select * from dl_1 where a=1 for update; … 1 row in set (0.00 sec)

    select * from dl_1 where a=1 for update;/* SQL2 */ 等待


    (session2 提示死锁回滚后,SQL1 成功返回结构)

    select * from dl where a=1 for update; ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

    commit;

    commit;

    这个实验也是两个 session 互相等待对方释放资源,就进入了死锁状态。


    4 间隙锁下的死锁

    session1

    session2

    set session transaction_isolation='REPEATABLE-READ'; * 设置会话隔离级别为 RR */

    set session transaction_isolation='REPEATABLE-READ'; * 设置会话隔离级别为 RR */

    begin;

    begin;

    select * from dl where a=1 for update; … 1 row in set (0.00 sec)

    select * from dl where a=2 for update; … 1 row in set (0.00 sec)

    insert into dl(a,b,c) values (2,3,3);/* SQL1 */ 等待


    (session2 提示死锁回滚后,SQL1 成功返回结果)

    insert into dl(a,b,c) values (1,4,4);/* SQL2 */ ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

    commit;

    commit;

    由于 RR 隔离级别下存在间隙锁,可以知道 SQL1 需要等待 a=2 获得的间隙锁,而 SQL2 需要等待 a=1 获得的间隙锁,两个 session 互相等待对方释放资源,就进入了死锁状态。


    5 INSERT 语句的死锁

    session1

    session2

    session3

    begin;



    insert into dl_insert(a,b,c) value (3,3,3);




    insert into dl_insert(a,b,c) value (3,3,3);/* 等待  */

    insert into dl_insert(a,b,c) value (3,3,3);/* 等待  */

    rollback;

    执行成功

    ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

    这里需要注意的是,a 字段有唯一索引。当 session1 执行完 insert 语句,会在索引 a=3 上加记录锁,当 session2 执行同样的 insert 语句时,唯一键冲突,加上读锁;同样 session3 也会加上读锁。

    当 session1 回滚,session2 和 session3 都试图继续执行插入操作,都要加上写锁。此时两个 session 都要等待对方的行锁,因此出现了死锁。


    一些死锁场景就介绍到这里,当然,也欢迎各位补充其他的一些死锁场景。

    本文已收录进 专栏《MySQL 高频面试题解析》

    相关文章:
    1. 一条 update 语句的生命历程
    2. 当前读和快照读的区别
    3. InnoDB 怎么做表空间迁移
    4. RR 隔离级别下真的不会产生幻读吗?
    5. MVCC 怎么实现的?
    6. 复制的演进历程
    7. 有哪些死锁场景?
    8. 物理备份和逻辑备份的区别

    最后修改时间:2020-11-12 18:51:58
    文章转载自悦专栏,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论

    文集目录
    暂无数据