Skip to content

Lock

InnoDB 锁算法

记录锁(Record Lock) 锁定单个行记录
间隙锁(Gap Lock) 锁定一个范围不包括记录本身
临键锁(Next-Key Lock) 锁定一个范围包含记录本身

InnoDB 锁特性

  • SELECT 操作的不可重复读问题通过 MVCC 解决
  • UPDATEDELETE 的不可重复读问题通过 Record Lock 解决
  • INSERT 的不可重复读问题通过 Next-Key Lock 解决

全局锁

使用场景

  • 全局逻辑备份(mysqldump

启动方法

  • 方法一(FTWRL)

    FLUSH TABLES WITH READ LOCK;
    
    -- UNLOCK TABLES;
    
  • 方法二

    SET GLOBAL readonly=true;
    

注意事项

  • 有些系统中 readonly 的值会被用来处理其它逻辑(如判断一个库是主库还是备库)

  • 如果执行 FTWRL 后由于客户端发生异常断开,MySQL 会自动释放这个全局锁,整个库回到可正常更新状态

  • 将库设置未 readonly 后,若客户端发生异常,数据库会一直保持 readonly 状态,导致整库长时间处于不可写状态

  • readonlySuper 权限无效

  • 为什么需要 FWTRL?

    • mysqldump 使用参数 --single-transaction 的时候,导数据之前就会启动一个事务来确保拿到一致性快照视图。而由于 MVCC 的支持,这个过程中数据是可以正常更新的。
    • 所以 --single-transaction 方法只适用于所有的表使用事务引擎的库。如果有的表使用了不支持事务的引擎,那么备份就只能通过 FTWRL 方法

表级锁

读写锁

  • 表共享读锁(共享锁)

    LOCK TABLES table_name READ;
    
  • 表独占写锁(排它锁)

    LOCK TABLES table_name WRITE;
    

元数据锁(MDL)

  • MDL 的作用是防止 DDL 和 DML 并发的冲突,保证读写的正确性
  • 不需要显式的使用 MDL,因为当我们对数据库表进行操作时,会自动给这个表加上 MDL

意向锁(Intention Lock)

  • 意向锁是表级锁,是 InnoDB 主动加的,不需要手动处理,其目的是为了快速判断表里是否有记录被加锁

    • 如果没有意向锁,那么加独占锁时,就需要遍历表里所有记录,查看是否有记录存在独占锁,这样效率会很低
    • 如果有了意向锁,在对记录加独占锁前,会先加上表级的意向独占锁,那么在加独占锁时,直接查该表是否有意向独占锁,如果有就意味着表里已经有记录被加了独占锁,这样就不用去遍历表里的记录
  • 对于 INSERTUPDATEDELETE,InnoDB 会自动给涉及的数据加排它锁

  • 对于一般的 SELECT 语句,InnoDB 不会加任何所,事务可以通过以下语句显示加共享锁或排它锁

    • 共享锁

      SELECT ... LOCK IN SHARE MODE;
      
    • 排它锁

      SELECT ... FOR UPDATE;
      
  • 意向共享锁(IS)是指当事务准备在某行记录上加共享锁(S)时,需要先在表级别加一个 IS 锁

  • 意向排它锁(IX)是指当事务准备在某行记录上加排它锁(X)时,需要先在表级别加一个 IX 锁

自增锁(AUTO-INC Lock)

  • 在为某个字段申明 AUTO_INCREMENT 属性后,可以在插入数据时不指定该字段的值,数据库会自动给该字段赋值递增的值,这主要是通过 AUTO-INC 锁实现的

  • AUTO-INC 锁是特殊的表锁机制,锁不是在一个事务提交后才释放,而是在执行完插入语句后就会立即释放

  • AUTO-INC 锁在对大量数据进行插入的时候,会影响插入性能,因为另一个事务中额插入会被阻塞

    • 因此,在 MySQL 5.1.22 版本开始,InnoDB 存储引擎提供了一种轻量级锁来实现自增

    • 一样是在插入数据的时候,会为被 AUTO_INCREMENT修饰的字段加上轻量级锁,给该字段赋值一个自增的值后就释放这个轻量级锁,而不需要等待整个插入语句执行完之后才释放锁

    • InnoDB 存储引擎提供了一个 innodb_autoinc_lock_mode 的系统变量,用来控制选择用 AUTO-INC锁还是轻量级锁

      innodb_autoinc_lock_mode=0 -- 采用 AUTO_INC 锁
      innodb_autoinc_lock_mode=1 -- 默认两种锁混用(如果能够确定插入的记录的数量就采用轻量级锁;不确定时就采用 AUTO_INC 锁)
      innodb_autoinc_lock_mode=2 -- 采用轻量级锁(性能最高,但自增长可能不连续,在主从复制场景中不安全)
      

页级锁

  • 页级锁是 MySQL 中锁定粒度介于行级锁和表级锁之间的一种锁
  • 表级锁速度快,但冲突多;行级锁冲突少,但速度慢;因此采取了折中的页级锁,一次锁定相邻的一组记录
  • BDB 存储引擎支持页级锁

行级锁

简介

  • 在 InnoDB 事务中,行锁通过给索引上的索引项加锁来实现
  • 只有通过索引条件检索数据,才会使用行级锁,否则将使用表级锁
  • 行级锁同样分为共享锁和排它锁,以及加锁前需要先获得的意向共享锁和意向排它锁
  • 行锁是在需要的时候才加上的,但并不是不需要了就立即释放,而是要等到事务结束时才释放

锁实现方式

记录锁(Record Lock)

  • 必须为唯一索引列或主键列,否则上述语句加的锁就会变成临键锁
  • 查询语句必须为精准匹配(=),否则也会退化为临键锁
  • 在 MySQL 中,行级锁并不是直接锁记录,而是锁索引
    • 如果一条语句操作了主键索引,MySQL 会锁定这条主键索引
    • 如果一条语句操作了非主键索引,MySQL 会先锁定该非主键索引,再锁定相关的主键索引

间隙锁(Gap Lock)

  • 间隙锁基于非唯一索引,它锁定一段范围内的索引记录
  • 产生间隙锁的条件
    • 使用普通索引锁定
    • 使用多列唯一索引
    • 使用唯一索引锁定多行记录

临键锁(Next-Key Lock)

  • 临键锁是一种特殊的间隙锁,通过临键锁可以解决幻读的问题
  • 每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右开区间的数据
  • InnoDB 中行级锁是基于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁