MySQL Innodb Insert语句加锁流程

MySQL版本:8.0.29
测试表:
在这里插入图片描述

测试数据:
在这里插入图片描述

事务A先执行:
在这里插入图片描述

加锁分析:

mysql> SELECT * FROM performance_schema.data_locks\G
*************************** 1. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 140346328944544:1066:140346432670032
ENGINE_TRANSACTION_ID: 3957
            THREAD_ID: 75
             EVENT_ID: 32
        OBJECT_SCHEMA: db_study
          OBJECT_NAME: user
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140346432670032
            LOCK_TYPE: TABLE
            LOCK_MODE: IX
          LOCK_STATUS: GRANTED
            LOCK_DATA: NULL
1 row in set (0.00 sec)

事务B执行:
在这里插入图片描述

加锁分析:

mysql> SELECT * FROM performance_schema.data_locks\G
*************************** 1. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 140346328944544:1066:140346432670032
ENGINE_TRANSACTION_ID: 3957
            THREAD_ID: 75
             EVENT_ID: 32
        OBJECT_SCHEMA: db_study
          OBJECT_NAME: user
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140346432670032
            LOCK_TYPE: TABLE
            LOCK_MODE: IX
          LOCK_STATUS: GRANTED
            LOCK_DATA: NULL
*************************** 2. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 140346328944544:5:5:5:140346462116384
ENGINE_TRANSACTION_ID: 3957
            THREAD_ID: 76
             EVENT_ID: 36
        OBJECT_SCHEMA: db_study
          OBJECT_NAME: user
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: age
OBJECT_INSTANCE_BEGIN: 140346462116384
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 96, 40
*************************** 3. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 140346328945336:1066:140346432671984
ENGINE_TRANSACTION_ID: 421821305655992
            THREAD_ID: 76
             EVENT_ID: 36
        OBJECT_SCHEMA: db_study
          OBJECT_NAME: user
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140346432671984
            LOCK_TYPE: TABLE
            LOCK_MODE: IS
          LOCK_STATUS: GRANTED
            LOCK_DATA: NULL
*************************** 4. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 140346328945336:5:5:5:140346462120992
ENGINE_TRANSACTION_ID: 421821305655992
            THREAD_ID: 76
             EVENT_ID: 36
        OBJECT_SCHEMA: db_study
          OBJECT_NAME: user
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: age
OBJECT_INSTANCE_BEGIN: 140346462120992
            LOCK_TYPE: RECORD
            LOCK_MODE: S
          LOCK_STATUS: WAITING
            LOCK_DATA: 96, 40
4 rows in set (0.00 sec)

事务A持有锁:
1)表user的意向排他锁
2)age索引:记录(9640)的排他锁

事务B持有锁:
1)表user的意向共享锁
2)age索引:范围(8996]的next-key lock

总结:
事务B想要获取age索引:范围(8996]的next-key lock,
但是因为事务A已经拥有了age索引:记录(9640)的排他锁,所以需要阻塞。
仔细思考,正常来说事务B想要当前读age=96的所有记录,
光是加这些锁不够,所以恢复运行后,加的锁又是不一样了,看下面。

事务A提交事务
在这里插入图片描述
事务B就可以查询到结果了
在这里插入图片描述
加锁分析:

mysql> SELECT * FROM performance_schema.data_locks\G
*************************** 1. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 140346328945336:1066:140346432671984
ENGINE_TRANSACTION_ID: 3962
            THREAD_ID: 76
             EVENT_ID: 36
        OBJECT_SCHEMA: db_study
          OBJECT_NAME: user
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140346432671984
            LOCK_TYPE: TABLE
            LOCK_MODE: IS
          LOCK_STATUS: GRANTED
            LOCK_DATA: NULL
*************************** 2. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 140346328945336:5:5:5:140346462121336
ENGINE_TRANSACTION_ID: 3962
            THREAD_ID: 76
             EVENT_ID: 37
        OBJECT_SCHEMA: db_study
          OBJECT_NAME: user
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: age
OBJECT_INSTANCE_BEGIN: 140346462121336
            LOCK_TYPE: RECORD
            LOCK_MODE: S
          LOCK_STATUS: GRANTED
            LOCK_DATA: 96, 40
*************************** 3. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 140346328945336:5:4:5:140346462121680
ENGINE_TRANSACTION_ID: 3962
            THREAD_ID: 76
             EVENT_ID: 37
        OBJECT_SCHEMA: db_study
          OBJECT_NAME: user
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 140346462121680
            LOCK_TYPE: RECORD
            LOCK_MODE: S,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 40
*************************** 4. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 140346328945336:5:5:2:140346462122024
ENGINE_TRANSACTION_ID: 3962
            THREAD_ID: 76
             EVENT_ID: 37
        OBJECT_SCHEMA: db_study
          OBJECT_NAME: user
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: age
OBJECT_INSTANCE_BEGIN: 140346462122024
            LOCK_TYPE: RECORD
            LOCK_MODE: S,GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 99, 10
4 rows in set (0.00 sec)

事务B持有的锁:
1)表user的意向共享锁
2)age索引:范围(8996]的next-key lock
3)主键索引:id=40的行共享锁
4)age索引:范围(9699)的gap lock

总结:
事务B刚开始获取的锁只是一个标记(阻塞在那里,
等着事务A释放行锁后恢复执行,因为行锁的获取单位的next-key lock,
记录锁和间隙锁是next-key lock的降级),恢复执行后,
需要重新上面这些锁,保证当前读

小结:insert如果没有并发,是不加锁,但底层会通过page latch保证并发修改同一page的线程安全。比如事务A要在某个间隙插入一条记录a,先会获取page x latch,然后插入数据(修改数据页),然后释放page x latch,还没有提交事务,此时事务B需要查询这个间隙中的数据,先获取page s latch,然后查询到记录a,通过记录a的隐藏字段发现这个记录是还没有提交的,所以给这个记录对应的事务加上行记录X锁,自己进入到这把锁的阻塞队列中,说白就是这个间隙有其他事务在操作,当前事务不能查询,需要先阻塞,等到前面的事务提交后才能继续查询,如果是select lock in share mode 进入S锁阻塞队列,如果是select for update 进入到X锁阻塞队列。
如果insert所在的事务获取page x latch后,发现这个间隙存在gap锁,会生成插入意向锁(插入意向锁是兼容的,本质上也是gap锁)然后阻塞,直到前面的gap锁释放

通过 LOCK_MODE 可以确认是 next-key 锁,还是间隙锁,还是记录锁:
如果 LOCK_MODE 为 X,说明是 next-key 锁;
如果 LOCK_MODE 为 X, REC_NOT_GAP,说明是记录锁;
如果 LOCK_MODE 为 X, GAP,说明是间隙锁;

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值