基于本文回答
0
评论

MySQL中有哪些锁?

知识点图片

本文详解MySQL锁机制,涵盖全局锁、表锁、InnoDB行锁(记录/间隙/临键)及锁模式分类。

这是一个关于MySQL锁的全面总结。MySQL的锁机制是其并发控制的核心,理解它对于数据库性能优化和解决并发问题至关重要。

我们将从不同的维度来对MySQL中的锁进行分类和解释。


一、 按锁的粒度划分 (Granularity)

锁的粒度决定了锁定的资源范围,粒度越小,并发度越高,但系统开销也越大。

1. 全局锁 (Global Lock)

全局锁锁定的是整个MySQL实例。当你需要让整个数据库处于只读状态时,可以使用它。

  • 命令: FLUSH TABLES WITH READ LOCK;
  • 效果: 执行后,整个数据库实例将处于只读状态。所有数据的更新语句(INSERT, UPDATE, DELETE)、表结构的修改语句(ALTER TABLE)等都会被阻塞。
  • 使用场景: 主要用于全库逻辑备份(如使用 mysqldump 时),以保证备份数据的一致性。
  • 释放: 连接断开或执行 UNLOCK TABLES;

2. 表级锁 (Table-level Lock)

表级锁锁定的是整张表,是MySQL中粒度最大、开销最小的锁。

  • 标准表锁 (Table Locks)

    • 命令: LOCK TABLES table_name [READ | WRITE];
    • 类型:
      • 表读锁 (Read Lock): 锁定后,所有客户端都可以读取该表,但当前客户端和其他客户端都不能修改该表。
      • 表写锁 (Write Lock): 锁定后,只有当前客户端可以读写该表,其他客户端的读写操作都会被阻塞。
    • 引擎: MyISAM存储引擎默认使用表级锁。InnoDB也支持,但一般不推荐手动使用,因为它会大大降低并发性。
  • 元数据锁 (Metadata Lock, MDL)

    • 机制: 从MySQL 5.5开始引入。MDL是自动的,不需要显式使用。当对一个表做增删改查(DML)时,会自动加上MDL读锁;当要修改表结构(DDL)时,会自动加上MDL写锁。
    • 作用: 保证元数据的一致性。防止在查询/修改表数据的同时,另一个线程修改了表结构。
    • 规则: 读锁之间不互斥,读锁和写锁互斥,写锁之间互斥。一个长事务(DML)会长期持有MDL读锁,导致DDL操作一直等待,可能拖垮整个库。
  • 意向锁 (Intention Lock)

    • 机制: 这是InnoDB引擎自动管理的表级锁,用于支持在不同粒度上进行加锁。它预示着事务准备要对表中的行加锁。
    • 类型:
      • 意向共享锁 (IS Lock): 事务打算给数据行加共享锁(S锁),必须先获得该表的IS锁。
      • 意向排他锁 (IX Lock): 事务打算给数据行加排他锁(X锁),必须先获得该表的IX锁。
    • 作用: 核心作用是协调行锁和表锁的关系。如果没有意向锁,当一个事务想对整个表加表锁时,它需要遍历表中的每一行,检查是否有行锁存在,效率极低。有了意向锁,它只需要检查表上是否有IX/IS锁即可,大大提高了效率。
  • 自增锁 (AUTO-INC Lock)

    • 机制: 一种特殊的表级锁,用于处理 AUTO_INCREMENT 列。当一个事务插入带有自增列的表时,会先获取这个锁,以保证分配的自增ID是连续且唯一的。
    • 模式: innodb_autoinc_lock_mode 参数可以控制其行为(连续、交错模式),在高并发插入时可能会影响性能。

3. 行级锁 (Row-level Lock)

行级锁锁定的是表中的某一行或多行数据。它的粒度最小,并发度最高,但开销也最大。这是 InnoDB 存储引擎的核心优势

InnoDB的行级锁是基于索引实现的。如果SQL语句没有使用到索引,InnoDB会将所有扫描到的行都锁定,可能会升级为表锁。

InnoDB行级锁主要有以下三种类型:

  • 记录锁 (Record Lock)

    • 定义: 这是最简单的行锁,直接锁定单个索引记录
    • 示例: SELECT * FROM t WHERE id = 1 FOR UPDATE; 会对 id=1 的这条索引记录加上记录锁。
  • 间隙锁 (Gap Lock)

    • 定义: 锁定一个范围,但不包括记录本身。它锁定的是索引记录之间的“间隙”。
    • 作用: 它的唯一目的就是防止幻读 (Phantom Read),保证了在可重复读(Repeatable Read)隔离级别下数据的一致性。
    • 示例: SELECT * FROM t WHERE id > 10 AND id < 20 FOR UPDATE; 会锁定 (10, 20) 这个开区间,防止其他事务在这个区间内插入新的数据。
    • 注意: 间隙锁之间是兼容的,即不同的事务可以同时在同一个间隙上持有间隙锁。
  • 临键锁 (Next-Key Lock)

    • 定义: 它是 记录锁 + 间隙锁 的组合。它会锁定一个索引记录本身,以及该记录之前的那个间隙。
    • 机制: 这是InnoDB在可重复读(Repeatable Read)隔离级别下的默认锁
    • 示例: 假设索引上有值 10, 20, 30。一个 WHERE id <= 20 的更新操作,会锁定 (10, 20] 这个左开右闭区间,即锁定记录20本身,以及10和20之间的间隙。
    • 作用: 同时解决了幻读和数据更新冲突的问题。
  • 插入意向锁 (Insert Intention Lock)

    • 定义: 这是一种特殊的间隙锁,在 INSERT 操作之前设置。它表明一个事务意图在某个间隙中插入数据,但并不会真的阻塞其他也想在这个间隙中插入数据的事务(只要它们插入的位置不冲突)。
    • 作用: 提高并发插入的性能。

二、 按锁的模式/兼容性划分 (Mode)

这是从另一个维度对锁进行分类,主要关注锁的兼容性。

1. 共享锁 (Shared Lock, S Lock)

又称读锁。一个事务获取了某行数据的共享锁后,其他事务可以再获取该行的共享锁(读读可以并行),但不可以获取该行的排他锁(读写阻塞)。

  • 使用: SELECT ... LOCK IN SHARE MODE;

2. 排他锁 (Exclusive Lock, X Lock)

又称写锁。一个事务获取了某行数据的排他锁后,其他事务不能再获取该行的任何锁(共享锁或排他锁),直到该锁被释放。

  • 使用: INSERT, UPDATE, DELETE 语句会自动加上排他锁。也可以手动添加,如 SELECT ... FOR UPDATE;

锁兼容性矩阵:

- X (排他) S (共享)
X (排他) 不兼容 不兼容
S (共享) 不兼容 兼容

三、 其他概念

1. 悲观锁 (Pessimistic Locking)

  • 理念: 总是假设最坏的情况,认为数据在任何时候都可能被其他事务修改。所以在操作数据之前,先获取锁,确保在自己操作期间数据不会被改变。
  • 实现: MySQL中的共享锁(S锁)和排他锁(X锁)都属于悲观锁的范畴。SELECT ... FOR UPDATE 就是典型的悲观锁应用。

2. 乐观锁 (Optimistic Locking)

  • 理念: 总是假设最好的情况,认为数据一般情况下不会产生冲突。所以在操作数据时不加锁,而是在提交更新时去检查在此期间数据是否被其他事务修改过。
  • 实现: 这通常不是数据库自带的锁机制,而是在应用层面实现。常用方法是:
    • 版本号机制: 在表中增加一个 version 字段。每次更新时,UPDATE ... SET version = version + 1 WHERE id = ? AND version = ?。如果WHERE条件中的version不匹配,说明数据已被修改,更新失败。
    • 时间戳机制: 类似于版本号,使用时间戳字段。

总结表格

锁类型 锁粒度 主要作用/特点 所属引擎/机制
全局锁 整个实例 全库逻辑备份,保证数据一致性 MySQL Server
表锁 整张表 开销小,并发低。MyISAM默认 MyISAM/InnoDB
元数据锁 (MDL) 整张表 保护表结构,防止DDL与DML冲突 MySQL Server
意向锁 (IS/IX) 整张表 协调行锁和表锁,提高加表锁效率 InnoDB
自增锁 (AUTO-INC) 整张表 保证自增ID的唯一和连续 InnoDB
记录锁 (Record Lock) 锁定单条索引记录 InnoDB
间隙锁 (Gap Lock) 行(范围) 锁定索引记录之间的间隙,防止幻读 InnoDB
临键锁 (Next-Key Lock) 行(记录+范围) 记录锁+间隙锁,RR级别下的默认锁 InnoDB

希望这份详细的总结能帮助你全面理解MySQL中的各种锁!

右滑查看面试常问