一网打尽MySQL各种锁

183 阅读4分钟

一、概述

或许您跟我一样,被MySQL各种各样的锁晃得眼花缭乱,接下来本文将对9种锁进行简单的分析,让读者对MySQL锁的整体框架有个清晰的认识。

  • 全局锁
  • 表级锁
    • 表锁
    • 元数据锁(MDL)
    • 意向锁(intention lock)
    • 自增锁(AUTO-INC)
  • 行级锁
    • 行锁
    • 间隙锁
    • 临键锁 next-key lock
    • 插入意向锁

二、正文

1、全局锁

  • 定义:全局锁就是对整个数据库实例加锁
  • 作用:让整个库进入只读状态,DML和DDL的增删改都会被阻塞
  • 命令:
    • 上锁:flush tables with read lock;
    • 释放锁:unlock tables;或者连接断开的时候也会自动释放锁
  • 应用:不支持事务的引擎如myisam,可以用全局锁做全库逻辑备份

2、表锁

  • 定义:对整张表上锁,不依赖于储存引擎(不管是MySQL的什么存储引擎,对于表锁的策略都是一样的)
  • 作用:对整个表上锁,分为读锁(共享锁、S锁)和写锁(排他锁、X锁)
  • 命令:
    • 上锁:lock tables 表名 read / write (读 / 写锁)
    • 释放锁:unlock tables;

3、元数据锁(MDL)

  • 定义:元数据即表结构,属于数据库隐式上锁(自动上锁),执行DML的时候上MDL读锁,执行DDL上MDL写锁
  • 作用:保证DDL和DML直接有序进行,保证读写正确性

4、意向锁(intention lock)

  • 定义:表明当前事务对表里面的行加锁的意向,上行锁之前就会上意向锁(自动上锁),分为意向排他锁(IX)和意向共享锁(IS),如果该行锁是读锁,则对应的意向锁就是IS,反之是IX
  • 作用:当一个事务想加表锁的时候,势必要确保表里面每一行都没有排他行锁(不管是共享表锁和排他表锁,都和排他行锁冲突),要对表的所有行进行遍历检查,如果数据量很大,则效率非常低。于是innodb推出意向锁,用以表示表是否存在行锁,提高表锁的效率。当表存在IX,则代表有某一行上了排他锁,那么将暂时无法上表锁
  • 加锁规则:
    • 在事务可以获取表中行的共享锁之前,它必须首先获取表上的 IS 锁
    • 在事务可以获取表中行的排他锁之前,它必须首先获取表上的 IX 锁
  • 备注:意向锁之间不会冲突,虽然它是表级锁,因为它表明的是行锁的意图。表锁和意向锁可能会冲突,只有共享表锁和IS能兼容,其他情况都会冲突

5、自增锁(AUTO-INC)

  • 定义:自增锁是一种特殊的表级别锁,专门针对事务插入AUTO_INCREMENT类型的列。最简单的情况,如果一个事务正在往表中插入记录,所有其他事务的插入必须等待,以便第一个事务插入的行,是连续的主键值。

6、行锁

  • 定义:锁住某一行的索引可以是普通索引,也可以是主键索引,分为写锁(X)和读锁(S)。读读兼容,读写冲突,写写冲突。
  • 作用:保证并发安全,粒度是某行数据
  • 命令:
    • 上锁:SELECT ... LOCK IN SHARE MODE(S)、普通查询语句(S)、SELECT ... FOR UPDATE(X)、UPDATE或DELETE或INSERT语句 (X)
    • 释放锁:事务结束
  • 备注:为什么给某行上了X锁之后,其他事务仍然能查询此行数据:因为MVCC一致性读,不同事务根据trx_id进行比较可以读到相应的数据版本

7、间隙锁

  • 定义:锁住索引之间的间隙,左开右开的区间,如(4,7)
  • 作用:在RR隔离级别下防止幻读出现(即锁住间隙,防止插入造成幻读)
  • 作用范围:大部分情况只在RR生效,在RC情况下在外键场景下会涉及
  • 互斥情况:同一间隙的间隙锁不互斥,但是会排斥往间隙insert的操作
  • 命令:
    • 上锁:上行锁的时候一起上
    • 释放锁:事务结束

8、临键锁 (next-key lock)

  • 定义:临键锁=记录锁+间隙锁,左开右闭区间,比如(4,7]
  • 作用:实际上,在RR隔离级别,我们通常说的加行锁,加的就是临键锁,但是有些情况临键锁会退化为间隙锁
  • 命令:
    • 上锁:上行锁的时候一起上
    • 释放锁:事务结束

9、插入意向锁

  • 定义:插入意向锁是一种在 INSERT 操作之前设置的一种间隙锁,插入意向锁表示了一种插入意图
  • 作用:当多个不同的事务,同时往同一个索引的同一个间隙中插入数据的时候,它们互相之间无需等待,即不会阻塞(要是单纯按照之前间隙锁的理论,必须要等一个间隙锁释放了,下一个事务才可以往相同的间隙处插入数据)
  • 命令:
    • 上锁:insert语句
    • 释放锁:事务结束