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

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

悦专栏 2020-11-11
2429

作者简介

马听,多年 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(11NOT NULL AUTO_INCREMENT,
    `a` int(11NOT NULL,
    `b` int(11NOT NULL,
    `c` int(11NOT 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进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论

    文集目录
    暂无数据