mysql中的锁模式

发布时间 2023-04-27 11:12:14作者: thep0st

1. innodb的事务锁模块

本文的目的是对 InnoDB 的事务锁模块做个简单的介绍,使读者对这块有初步的认识。本文先介绍行级锁和表级锁的相关概念,再介绍其内部的一些实现;最后以两个有趣的案例结束本文。

2.行级锁

InnoDB 支持到行级别粒度的并发控制,本小节我们分析下几种常见的行级锁类型,以及在哪些情况下会使用到这些类型的锁。

2.1 行级锁-LOCK_REC_NOT_GAP

# 也就是简单的行锁
锁带上这个 FLAG 时,表示这个锁对象只是单纯的锁在记录上,不会锁记录之前的 GAP。
在 RC 隔离级别下一般加的都是该类型的记录锁(但唯一二级索引上的 duplicate key 检查除外,总是加 LOCK_ORDINARY 类型的锁)。

2.2 行级锁-LOCK_GAP

# 也就是gap锁
表示只锁住一段范围,不锁记录本身,通常表示两个索引记录之间,或者索引上的第一条记录之前,或者最后一条记录之后的锁。可以理解为一种区间锁,一般在RR隔离级别下会使用到GAP锁。
你可以通过切换到RC隔离级别,或者开启选项innodb_locks_unsafe_for_binlog来避免GAP锁。
这时候只有在检查外键约束或者duplicate key检查时才会使用到GAP LOCK。

2.3 行级锁-LOCK_ORDINARY(Next-Key Lock)

# 也就是所谓的 NEXT-KEY 锁,包含记录本身及记录之前的GAP。锁住行,以及区间
当前 MySQL 默认情况下使用RR的隔离级别,而NEXT-KEY LOCK正是为了解决RR隔离级别下的幻读问题。所谓幻读就是一个事务内执行相同的查询,会看到不同的行记录。在RR隔离级别下这是不允许的。

假设索引上有记录1, 4, 5, 8,12 我们执行类似语句:SELECT… WHERE col > 10 FOR UPDATE。
如果我们不在(8, 12)之间加上Gap锁,另外一个 Session 就可能向其中插入一条记录,例如9,再执行一次相同的SELECT FOR UPDATE,就会看到新插入的记录。 这也是为什么插入一条记录时,需要判断下一条记录上是否加锁了。

2.4 行级锁-LOCK_S(共享锁)

共享锁的作用通常用于在事务中读取一条行记录后,不希望它被别的事务锁修改,但所有的读请求产生的LOCK_S锁是不冲突的。
在InnoDB里有如下几种情况会请求S锁:
第一种
普通查询在隔离级别为 SERIALIZABLE 会给记录加 LOCK_S 锁。但这也取决于场景。非事务读(auto-commit)在 SERIALIZABLE 隔离级别下,无需加锁
# 不过在当前最新的5.7.10版本中,SHOW ENGINE INNODB STATUS 的输出中不会打印只读事务的信息,只能从informationschema.innodb_trx表中获取到该只读事务持有的锁个数等信息。
第二种
类似 SQL SELECT … IN SHARE MODE,会给记录加S锁,其他线程可以并发查询,但不能修改。基于不同的隔离级别,行为有所不同:
  RC隔离级别: 
    LOCK_REC_NOT_GAP | LOCK_S;
  RR隔离级别:
    如果查询条件为唯一索引且是唯一等值查询时,加的是 LOCK_REC_NOT_GAP | LOCK_S;
    对于非唯一条件查询,或者查询会扫描到多条记录时,加的是LOCK_ORDINARY | LOCK_S锁,也就是记录本身+记录之前的GAP;

 




 

 

pass