首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

十一,MySQL锁机制详解

  • 25-04-20 22:01
  • 3635
  • 10919
juejin.cn

一,介绍

MySQL的锁机制是保证数据一致性和并发控制的核心组件,尤其在多用户环境下至关重要。

按锁粒度划分

  1. 全局锁(Global Lock)

    • 作用:锁定整个数据库实例,确保备份时数据一致性。
  2. 表级锁(Table-Level Lock)

    • 特点:锁定整张表,开销小、加锁快,但并发度低。
  3. 行级锁(Row-Level Lock)

    • 特点:锁定单行或多行,开销大、加锁慢,但并发度高。

二,全局锁

2.1 介绍

全局锁就是对整个数据库实例加锁。

枷锁后整个实例就处于只读状态,后续的增删改操作,事务提交操作,修改表操作等会处于阻塞状态。全局锁一般用在数据库的逻辑备份,对所有表进行锁定,从而获取一致性视图,确保数据的完整性。

2.2 语法

加全局锁

sql
代码解读
复制代码
flush tables with read lock;

解锁

sql
代码解读
复制代码
unlock tables;

2.3 数据库备份(mysqldump)

bash
代码解读
复制代码
mysqldump -u账号 -p密码 -h主机地址 要备份的数据库 > 备份的路径/xxx.sql

mysqldump是mysql提供的一个备份工具,直接在命令行执行即可不需要进入mysql bash,xxx.sql文件存储的是SQL语句!

三,表级锁

3.1 介绍

  1. 锁定粒度:锁定整张表,无论操作涉及多少行数据。
  2. 加锁速度:开销小,加锁快(仅需维护表级别的锁状态)。
  3. 并发性能:并发度低,同一时间只能有一个事务对表进行写操作。
  4. 适用存储引擎:
    • MyISAM:默认使用表级锁(仅支持表级锁)。
    • InnoDB:支持行级锁,但在某些场景(如无索引的全表扫描)会退化为表级锁。

3.2 表级锁类型

  1. 表共享读锁(S Lock):
    • 允许其他会话读表,但阻塞所有写操作。
    • 显式加锁:LOCK TABLES table_name READ;
  2. 表独占写锁(X Lock):
    • 独占访问,阻塞其他会话的读写操作。
    • 显式加锁:LOCK TABLES table_name WRITE;
  3. 元数据锁(MDL):
    • 隐式锁,保护表结构变更(DDL操作)。
    • 读锁(SHARED_READ)与写锁(EXCLUSIVE)的互斥规则严格。
  4. 意向锁(Intention Lock):
    • 意向共享锁(IS):事务计划对某些行加共享锁(S)。
    • 意向排他锁(IX):事务计划对某些行加排他锁(X)。

3.3 表共享读锁\表独占写锁

3.3.1 表共享读锁(Shared Read Lock, S Lock)

  • 作用:允许多个事务同时读取表数据,但阻塞所有写操作。
  • 加锁方式:
    • 显式加锁:LOCK TABLES table_name READ;
    • 隐式加锁(仅MyISAM引擎):执行SELECT时自动加读锁。
  • 兼容性:
    • 允许其他事务加读锁(共享读)。
    • 阻塞其他事务加写锁(互斥写)。
  • 释放条件:
    • 显式释放:UNLOCK TABLES;
    • 隐式释放(MyISAM):查询结束后自动释放(非事务引擎)。

加锁流程示例(MyISAM引擎)

场景:会话A加表共享读锁,会话B尝试读写。

sql
代码解读
复制代码
-- 会话A(显式加读锁) LOCK TABLES users READ; -- 会话A执行读操作(允许) SELECT * FROM users WHERE id=1; -- 会话B尝试读操作(允许) SELECT * FROM users WHERE id=2; -- 成功执行 -- 会话B尝试写操作(阻塞) UPDATE users SET name='Bob' WHERE id=1; -- 等待会话A释放锁 -- 会话A释放锁 UNLOCK TABLES; -- 会话B的UPDATE操作继续执行

结果:

  • 会话B的读操作(SELECT)不受影响。
  • 会话B的写操作(UPDATE)被阻塞,直到会话A释放锁。

3.3.2 表独占写锁(Exclusive Write Lock, X Lock)

  • 作用:独占整个表,禁止其他事务读写。
  • 加锁方式:
    • 显式加锁:LOCK TABLES table_name WRITE;
    • 隐式加锁(仅MyISAM引擎):执行INSERT/UPDATE/DELETE时自动加写锁。
  • 兼容性:
    • 阻塞其他事务的所有读写操作(完全独占)。
  • 释放条件:
    • 显式释放:UNLOCK TABLES;
    • 隐式释放(MyISAM):写操作完成后自动释放。

加锁流程示例(MyISAM引擎)

场景:会话A加表独占写锁,会话B尝试读写。

sql
代码解读
复制代码
-- 会话A(显式加写锁) LOCK TABLES users WRITE; -- 会话A执行写操作(允许) UPDATE users SET name='Alice' WHERE id=1; -- 会话B尝试读操作(阻塞) SELECT * FROM users WHERE id=2; -- 等待会话A释放锁 -- 会话B尝试写操作(阻塞) INSERT INTO users (id, name) VALUES (3, 'Charlie'); -- 同样被阻塞 -- 会话A释放锁 UNLOCK TABLES; -- 会话B的SELECT和INSERT操作继续执行

结果:会话B的所有读写操作均被阻塞,直到会话A释放锁。


3.3.3 锁的兼容性与冲突规则

当前锁类型 \ 请求锁类型共享读锁(S)独占写锁(X)
共享读锁(S)✅ 兼容❌ 不兼容
独占写锁(X)❌ 不兼容❌ 不兼容
  • 核心规则:
    • 读锁之间共享,写锁完全独占。
    • 写锁优先级高于读锁(等待队列中写锁可能优先获取)。

3.3.4 InnoDB中的表级锁退化

InnoDB默认使用行级锁,但在以下场景会退化为表级锁:

  1. 无索引的更新/删除:
    sql
    代码解读
    复制代码
    -- 若age字段无索引,退化为表锁 UPDATE users SET status=1 WHERE age > 30;
  2. 显式锁表:
    sql
    代码解读
    复制代码
    LOCK TABLES users WRITE; -- 强制使用表级锁(破坏InnoDB行锁机制)

示例:InnoDB无索引导致表锁

sql
代码解读
复制代码
-- 会话A(无索引UPDATE) BEGIN; UPDATE users SET status=1 WHERE age > 30; -- age字段无索引,退化为表锁 -- 会话B尝试更新其他行(被阻塞) UPDATE users SET name='Dave' WHERE id=5; -- 因表锁被阻塞,直到会话A提交

3.3.5 监控锁状态

  1. 查看当前锁信息
go
代码解读
复制代码
```sql
sql
代码解读
复制代码
SHOW OPEN TABLES WHERE In_use > 0; -- 查看被锁定的表 SHOW PROCESSLIST; -- 查看阻塞的会话 ```

2. MyISAM锁监控

sql
代码解读
复制代码
```sql SHOW STATUS LIKE 'Table_locks%'; -- Table_locks_immediate: 立即获取锁的次数 -- Table_locks_waited: 需要等待锁的次数(值高表示锁竞争严重) ```

3. InnoDB锁监控(需启用InnoDB监控)

sql
代码解读
复制代码
```sql -- 查询INFORMATION_SCHEMA表 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; -- 当前持有的锁 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; -- 锁等待关系 ```

3.3.6 总结

  • 表共享读锁(S):允许多读,禁写,适合读多写少场景。
  • 表独占写锁(X):完全独占,适合批量写入或表结构变更。
  • 注意事项:
    • MyISAM依赖表级锁,InnoDB慎用显式锁表。
    • 避免长事务持有锁,减少锁竞争。
    • 合理设计索引,防止InnoDB退化为表锁。

3.4 元数据锁

3.4.1 元数据锁(MDL)的核心作用

  • 保护表结构:防止在数据操作(DML)过程中,表结构(Schema)被意外修改(如ALTER TABLE、DROP TABLE),导致数据不一致。
  • 隐式自动管理:由MySQL自动加锁,无需用户手动干预。
  • 所有存储引擎生效:无论是InnoDB、MyISAM还是其他引擎,均受MDL约束。

3.4.2 MDL锁的类型与行为

锁类型触发场景兼容性规则
MDL读锁(SHARED_READ)SELECT、UPDATE、DELETE等DML操作允许多个会话同时持有,但阻塞DDL操作。
MDL写锁(EXCLUSIVE)ALTER TABLE、DROP TABLE等DDL操作独占锁,阻塞所有其他会话的DML和DDL操作

3.4.3 MDL锁的典型问题场景

  1. 长事务阻塞DDL操作

    sql
    代码解读
    复制代码
    -- 会话A(长事务) BEGIN; SELECT * FROM users; -- 隐式加MDL读锁 -- 不提交事务... -- 会话B(DDL操作) ALTER TABLE users ADD COLUMN age INT; -- 需要MDL写锁,被阻塞
  • 结果:会话B的ALTER TABLE会一直等待,直到会话A提交或超时(默认50秒)。
  1. DDL操作阻塞后续查询

    sql
    代码解读
    复制代码
    -- 会话A(DDL操作) ALTER TABLE users ADD COLUMN email VARCHAR(255); -- 加MDL写锁 -- 会话B(普通查询) SELECT * FROM users; -- 需要MDL读锁,被阻塞
  • 结果:会话B的SELECT在DDL完成前无法执行。

3.4.4 总结

  • MDL锁是隐式锁:自动保护表结构,避免DDL与DML并发导致数据不一致。
  • 核心问题:长事务和DDL操作的冲突是MDL锁问题的根源。
  • 优化关键:
    • 及时提交事务,避免长查询。
    • 使用在线DDL工具减少锁冲突。
    • 监控锁等待和会话状态。

3.5 意向锁

3.5.1 意向锁的核心作用

意向锁是 表级锁,用于协调 行级锁 与 表级锁 的共存,解决以下问题:

  1. 避免全表扫描检查行锁: 当需要加表级锁时,无需逐行检查是否有行锁冲突,只需检查意向锁即可快速判断。
  2. 支持多粒度锁共存: 允许行级锁(细粒度)和表级锁(粗粒度)同时存在,提升并发效率。
  3. 减少锁冲突概率: 通过层级锁机制(意向锁 -> 行锁),降低锁竞争复杂度。

3.5.2 意向锁的类型

锁类型缩写行为
意向共享锁IS表示事务计划对表中的某些行加 共享锁(S Lock)(读操作)。
意向排他锁IX表示事务计划对表中的某些行加 排他锁(X Lock)(写操作)

3.5.3 意向锁的兼容性规则

意向锁之间的兼容性决定了多个事务能否同时操作同一张表:

当前锁类型 \ 请求锁类型ISIX表级S锁(读锁)表级X锁(写锁)
IS✅✅✅❌
IX✅✅❌❌
表级S锁(S)✅❌✅❌
表级X锁(X)❌❌❌❌
  • 关键规则:
    • IS与IS、IX、表级S锁兼容:允许多个事务同时读取表的不同行。
    • IX与IS、IX兼容:允许多个事务同时修改不同行。
    • 表级S锁/X锁与意向锁互斥:表级锁需要独占访问,必须等待意向锁释放。

3.5.4 加锁流程与示例

1. 加锁规则

  • 层级加锁:事务在加行锁前,必须先对表加对应的意向锁(IS或IX)。
    • 加行级S锁前 → 先加IS锁。
    • 加行级X锁前 → 先加IX锁。

2. 示例场景

  • 场景1:事务A读取某一行(加IS锁)

    sql
    代码解读
    复制代码
    -- 事务A BEGIN; SELECT * FROM users WHERE id=1 LOCK IN SHARE MODE; -- 对表加IS锁,对id=1加行级S锁 -- 其他事务: -- 可以继续对表加IS锁(如SELECT其他行),但无法加表级X锁。
  • 场景2:事务B修改某一行(加IX锁)

    sql
    代码解读
    复制代码
    -- 事务B BEGIN; UPDATE users SET name='Alice' WHERE id=2; -- 对表加IX锁,对id=2加行级X锁 -- 其他事务: -- 可以加IS锁(读其他行),但无法加表级S锁或X锁。
  • 场景3:事务C尝试加表级S锁

    sql
    代码解读
    复制代码
    -- 事务C LOCK TABLES users READ; -- 请求表级S锁 -- 若存在活跃的IX锁(如事务B未提交),事务C会被阻塞,直到所有IX锁释放。

3.5.5 意向锁的意义与优势

  1. 性能优化: 避免在加表级锁时遍历所有行检查锁状态,减少资源消耗。
  2. 锁粒度协调: 允许行级锁和表级锁共存,例如:
    • 一个事务持有行级X锁(IX锁),另一个事务可持有表级S锁(只要没有IX冲突)。
  3. 死锁预防: 层级锁机制(意向锁先行)减少了循环等待的可能性。

3.5.6 意向锁的监控

通过InnoDB系统表查看意向锁状态:

sql
代码解读
复制代码
-- 查看当前持有的锁(包括意向锁) SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; -- 查看锁等待关系 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

3.5.7 总结

  • 意向锁是InnoDB实现多粒度锁的核心机制,平衡了并发性能与数据一致性。
  • IS锁:预示事务要读取某些行。
  • IX锁:预示事务要修改某些行。
  • 核心价值:通过层级锁机制,高效管理行锁与表锁的共存,避免全表扫描检查锁冲突。

四,行级锁

4.1 介绍

行级锁是InnoDB存储引擎的核心特性,用于在 高并发事务 中实现细粒度的数据访问控制,解决以下问题:

  1. 提升并发性能:仅锁定需要操作的行,而非整张表。
  2. 避免锁冲突:不同事务可以同时操作不同行,减少阻塞。
  3. 支持复杂事务:结合事务隔离级别(如REPEATABLE READ),实现ACID特性。

4.2 记录锁

4.2.1 记录锁的定义与作用

  • 定义:记录锁是 行级锁 的一种,锁定索引中的 单条记录。
  • 核心作用:
    1. 确保事务对单行数据的 独占访问 或 共享访问。
    2. 防止其他事务并发修改同一行数据,保证数据一致性。

4.2.2 记录锁的触发场景

  1. 显式加锁

    通过SELECT ... FOR UPDATE(排他锁)或SELECT ... LOCK IN SHARE MODE(共享锁)显式加锁。

    sql
    代码解读
    复制代码
    -- 排他锁(X Lock) BEGIN; SELECT * FROM users WHERE id=1 FOR UPDATE; -- 对id=1加记录X锁 -- 共享锁(S Lock) BEGIN; SELECT * FROM users WHERE id=1 LOCK IN SHARE MODE; -- 对id=1加记录S锁
  2. 隐式加锁

    DML操作:UPDATE、DELETE语句自动对符合条件的行加 排他锁。

    sql
    代码解读
    复制代码
    BEGIN; UPDATE users SET name='Alice' WHERE id=1; -- 自动对id=1加记录X锁

4.2.3 记录锁的兼容性规则

当前锁类型 \ 请求锁类型共享锁(S)排他锁(X)
共享锁(S)✅ 兼容❌ 不兼容
排他锁(X)❌ 不兼容❌ 不兼容
  • 共享锁(S):允许多个事务同时读取同一行。
  • 排他锁(X):独占访问,阻塞其他事务的所有操作。

4.2.4 记录锁的加锁流程示例

  • 场景1:共享锁(S)与共享锁(S)兼容

    sql
    代码解读
    复制代码
    -- 事务A BEGIN; SELECT * FROM users WHERE id=1 LOCK IN SHARE MODE; -- 加S锁 -- 事务B BEGIN; SELECT * FROM users WHERE id=1 LOCK IN SHARE MODE; -- 允许加S锁,正常读取
  • 场景2:共享锁(S)与排他锁(X)冲突

    sql
    代码解读
    复制代码
    -- 事务A BEGIN; SELECT * FROM users WHERE id=1 LOCK IN SHARE MODE; -- 加S锁 -- 事务B BEGIN; UPDATE users SET name='Bob' WHERE id=1; -- 尝试加X锁,被阻塞,直到事务A提交
  • 场景3:排他锁(X)完全独占

    sql
    代码解读
    复制代码
    -- 事务A BEGIN; UPDATE users SET name='Alice' WHERE id=1; -- 自动加X锁 -- 事务B BEGIN; SELECT * FROM users WHERE id=1 FOR UPDATE; -- 尝试加X锁,被阻塞

4.2.5 记录锁的底层实现

  • 依赖索引:记录锁基于索引实现,若查询条件无索引,InnoDB会退化为 表级锁。
  • 锁定索引记录:
    • 若表有主键,直接锁定主键索引。
    • 若表无主键但有唯一索引,锁定唯一索引。
    • 若表无索引,锁定隐藏的DB_ROW_ID(退化为表锁)。

示例:无索引导致表锁

sql
代码解读
复制代码
-- 假设age字段无索引 BEGIN; UPDATE users SET status=1 WHERE age=30; -- 无索引,退化为表级锁

4.2.6 记录锁的监控与诊断

  1. 查看当前记录锁

    sql
    代码解读
    复制代码
    -- 查看InnoDB锁信息(MySQL 5.7+) SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS WHERE LOCK_TYPE = 'RECORD'; -- 输出示例: -- LOCK_ID: 1234:5:3:2 -- LOCK_TRX_ID: 1234 -- 事务ID -- LOCK_MODE: X,REC_NOT_GAP -- 排他记录锁(非间隙) -- LOCK_DATA: 1 -- 锁定的记录值(如id=1)
  2. 分析锁等待

    sql
    代码解读
    复制代码
    -- 查看锁等待关系 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; -- 输出示例: -- requesting_trx_id: 5678 -- 请求锁的事务ID -- blocking_trx_id: 1234 -- 持有锁的事务ID
  3. 死锁日志分析

    sql
    代码解读
    复制代码
    SHOW ENGINE INNODB STATUS; -- 查看最近死锁信息

在输出日志的LATEST DETECTED DEADLOCK部分,可找到死锁涉及的记录锁详情。

4.2.7 总结

  • 记录锁是行级锁的基石:直接控制单行数据的并发访问。
  • 显式与隐式加锁:通过FOR UPDATE或DML操作触发,需注意索引设计。
  • 优化核心:索引、事务控制、死锁处理。

4.3 间隙锁

4.3.1 间隙锁的定义与作用

  • 定义:间隙锁是 行级锁 的一种,锁定索引记录之间的 间隙(即一个范围,不包含记录本身)。
  • 核心作用:
    1. 防止幻读(Phantom Read):确保事务执行过程中,其他事务无法在锁定的间隙内插入新数据。
    2. 范围保护:锁定查询条件覆盖的区间,保证事务多次读取的范围结果一致。
  • 生效条件:仅在 REPEATABLE READ 隔离级别下生效(InnoDB默认级别)。

image

4.3.2 间隙锁的触发场景

  1. 范围查询

    sql
    代码解读
    复制代码
    -- 锁定id在(5, 10)之间的间隙(不包含5和10) SELECT * FROM users WHERE id BETWEEN 5 AND 10 FOR UPDATE;
  2. 非唯一索引的等值查询

    sql
    代码解读
    复制代码
    -- 假设age是非唯一索引 SELECT * FROM users WHERE age = 25 FOR UPDATE; -- 锁定age=25前后的间隙
  3. 唯一索引的间隙查询

    sql
    代码解读
    复制代码
    -- 若表中没有id=7的记录,则锁定id=5到id=10的间隙 SELECT * FROM users WHERE id=7 FOR UPDATE;

4.3.3 间隙锁的兼容性规则

当前锁类型 \ 请求锁类型共享间隙锁(S)排他间隙锁(X)插入意向锁
共享间隙锁(S)✅ 兼容✅ 兼容❌ 不兼容
排他间隙锁(X)✅ 兼容✅ 兼容❌ 不兼容
插入意向锁❌ 不兼容❌ 不兼容✅ 兼容
  • 关键规则:
    • 间隙锁之间完全兼容,允许多个事务同时锁定同一间隙。
    • 间隙锁与插入意向锁互斥,插入操作会被阻塞。

4.3.4 间隙锁的底层实现

  • 基于索引:间隙锁仅作用于索引,若表无索引,InnoDB会创建隐藏的聚簇索引(DB_ROW_ID)来实现。
  • 锁定范围:
    • 对于唯一索引的等值查询(命中记录),退化为记录锁(无间隙锁)。
    • 对于非唯一索引或未命中记录的查询,锁定间隙。

示例1:非唯一索引触发间隙锁

sql
代码解读
复制代码
-- 表结构:id(主键), age(非唯一索引) -- 数据:id=1(age=20), id=3(age=25), id=5(age=30) -- 事务A BEGIN; SELECT * FROM users WHERE age=25 FOR UPDATE; -- 锁定age=25的间隙:(20,25)和(25,30)

示例2:唯一索引未命中记录触发间隙锁

sql
代码解读
复制代码
-- 表数据:id=5, 10, 15(主键) -- 事务A BEGIN; SELECT * FROM users WHERE id=7 FOR UPDATE; -- 锁定间隙:(5,10)

4.3.5 间隙锁与幻读的解决

  • 幻读现象:事务A两次读取同一范围,期间事务B插入新数据,导致事务A第二次读取出现“幻行”。
  • 间隙锁的作用:
    sql
    代码解读
    复制代码
    -- 事务A(REPEATABLE READ隔离级别) BEGIN; SELECT * FROM users WHERE age > 20 FOR UPDATE; -- 锁定age>20的间隙 -- 事务B INSERT INTO users (age) VALUES (25); -- 被阻塞,直到事务A提交
    • 事务A的间隙锁阻止了事务B的插入,确保两次查询结果一致。

4.3.6 间隙锁的监控与诊断

  1. 查看当前间隙锁

    sql
    代码解读
    复制代码
    -- 查看InnoDB锁信息(MySQL 5.7+) SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS WHERE LOCK_MODE LIKE '%GAP%'; -- 输出示例: -- LOCK_ID: 1234:5:3:2 -- LOCK_TYPE: RECORD -- LOCK_MODE: X,GAP -- 排他间隙锁 -- LOCK_DATA: 10 -- 锁定的间隙上限(如id=10)
  2. 分析锁等待

    sql
    代码解读
    复制代码
    -- 查看被阻塞的插入操作 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
  3. 死锁日志分析

    sql
    代码解读
    复制代码
    SHOW ENGINE INNODB STATUS;

    在日志的LATEST DETECTED DEADLOCK部分,可找到间隙锁冲突导致的死锁信息。

4.3.7 总结

  • 间隙锁是防止幻读的核心机制:通过锁定索引间隙,确保事务范围内的数据一致性。
  • 触发条件:范围查询、非唯一索引查询、唯一索引未命中记录。
  • 优化核心:索引设计、事务隔离级别选择、避免长事务。

4.3 临间锁

4.3.1 临键锁的定义与作用

  • 定义:临键锁是 行级锁 的一种,由 记录锁(Record Lock) 和 间隙锁(Gap Lock) 组合而成,锁定索引记录及其 之前的间隙。
  • 核心作用:
    1. 解决幻读(Phantom Read):确保事务执行过程中,其他事务无法在锁定范围内插入新数据。
    2. 范围保护:锁定查询条件覆盖的记录及间隙,保证事务多次读取的范围结果一致。
  • 默认机制:InnoDB在 REPEATABLE READ 隔离级别下默认使用临键锁。

4.3.2 临键锁的触发场景

  1. 范围查询

    sql
    代码解读
    复制代码
    -- 锁定id >= 10的记录及之前的间隙 SELECT * FROM users WHERE id >= 10 FOR UPDATE;
  2. 非唯一索引的等值查询

    sql
    代码解读
    复制代码
    -- 假设age是非唯一索引,锁定age=25的记录及前后间隙 SELECT * FROM users WHERE age = 25 FOR UPDATE;
  3. 未命中记录的查询

    sql
    代码解读
    复制代码
    -- 若表中无id=7的记录,锁定(5,10)的间隙及记录(临键锁) SELECT * FROM users WHERE id=7 FOR UPDATE;

4.3.3 临键锁的组成与范围

  • 组成:
    • 记录锁:锁定索引中的一条记录(如果存在)。
    • 间隙锁:锁定该记录之前的间隙。
  • 锁定范围:
    • 若查询条件命中记录:锁定该记录及其之前的间隙。
    • 若未命中记录:仅锁定间隙(退化为间隙锁)。

示例:

sql
代码解读
复制代码
-- 表数据:id=5, 10, 15(主键) -- 事务A BEGIN; SELECT * FROM users WHERE id BETWEEN 10 AND 20 FOR UPDATE;
  • 锁定范围:
    • 记录锁:id=10、15(若存在)。
    • 间隙锁:(10, 15)、(15, 20)。
  • 总锁定区间:[10, +∞)。

4.3.4 临键锁的兼容性规则

当前锁类型 \ 请求锁类型临键锁(X)共享锁(S)插入意向锁
临键锁(X)❌ 不兼容❌ 不兼容❌ 不兼容
共享锁(S)❌ 不兼容✅ 兼容✅ 兼容
插入意向锁❌ 不兼容✅ 兼容✅ 兼容
  • 关键规则:
    • 临键锁(X)与其他锁完全互斥,保证独占性。
    • 插入意向锁与临键锁互斥,插入操作需等待。

4.3.5 临键锁与幻读的解决

  • 幻读现象:事务A两次读取同一范围,事务B插入新数据,导致事务A第二次读取出现“幻行”。
  • 临键锁的作用:
    sql
    代码解读
    复制代码
    -- 事务A(REPEATABLE READ隔离级别) BEGIN; SELECT * FROM users WHERE id > 10 FOR UPDATE; -- 加临键锁,锁定(10, +∞) -- 事务B INSERT INTO users (id) VALUES (12); -- 被阻塞,直到事务A提交
    • 事务A的临键锁阻止了事务B的插入,确保两次查询结果一致。

4.3.6 临键锁的监控与诊断

  1. 查看当前临键锁

    sql
    代码解读
    复制代码
    -- 查看InnoDB锁信息(MySQL 5.7+) SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS WHERE LOCK_MODE LIKE 'X%'; -- 临键锁模式为X或IX -- 输出示例: -- LOCK_ID: 1234:5:3:2 -- LOCK_TYPE: RECORD -- LOCK_MODE: X -- 临键锁(默认模式) -- LOCK_DATA: 10 -- 锁定的记录值
  2. 分析锁等待

    sql
    代码解读
    复制代码
    -- 查看被临键锁阻塞的操作 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
  3. 死锁日志分析

    sql
    代码解读
    复制代码
    SHOW ENGINE INNODB STATUS;

    在日志LATEST DETECTED DEADLOCK`部分,可找到临键锁导致的死锁信息。

4.3.7 临键锁的示例场景

  • 场景1:范围查询触发临键锁

    sql
    代码解读
    复制代码
    -- 表数据:id=5, 10, 15 -- 事务A BEGIN; SELECT * FROM users WHERE id > 10 FOR UPDATE; -- 锁定(10, 15), (15, +∞) -- 事务B INSERT INTO users (id) VALUES (12); -- 被阻塞 UPDATE users SET name='Bob' WHERE id=15; -- 被阻塞
  • 场景2:非唯一索引触发临键锁

    sql
    代码解读
    复制代码
    -- 表结构:id(主键), age(非唯一索引) -- 数据:id=1(age=20), id=3(age=25), id=5(age=30) -- 事务A BEGIN; SELECT * FROM users WHERE age >= 25 FOR UPDATE; -- 锁定age=25的记录及(25,30)的间隙 -- 事务B INSERT INTO users (age) VALUES (28); -- 被阻塞

4.3.8 总结

  • 临键锁是InnoDB的默认锁机制:在REPEATABLE READ级别下,通过组合记录锁和间隙锁彻底解决幻读。
  • 锁定范围:索引记录及其之前的间隙,确保事务范围内的数据一致性。
  • 优化核心:索引设计、查询条件控制、事务拆分。

4.4 插入意向锁

4.4.1 插入意向锁的定义与作用

  • 定义:插入意向锁是 间隙锁(Gap Lock) 的一种特殊类型,在插入新数据前对目标间隙加锁,表示“准备在此间隙插入数据”。
  • 核心作用:
    1. 优化并发插入:允许多个事务在同一间隙的不同位置插入数据(只要不冲突),提高插入效率。
    2. 避免锁冲突:与普通间隙锁不同,插入意向锁之间互相兼容,仅在目标位置冲突时阻塞。

4.4.2 插入意向锁的触发场景

  1. 插入操作前隐式加锁

    sql
    代码解读
    复制代码
    -- 事务A尝试插入数据 BEGIN; INSERT INTO users (id) VALUES (15); -- 对间隙(10,20)加插入意向锁
  2. 与间隙锁的冲突

    sql
    代码解读
    复制代码
    -- 事务A已锁定间隙(10,20) BEGIN; SELECT * FROM users WHERE id BETWEEN 10 AND 20 FOR UPDATE; -- 加间隙锁 -- 事务B尝试插入数据 BEGIN; INSERT INTO users (id) VALUES (15); -- 需获取插入意向锁,但被事务A的间隙锁阻塞

4.4.3 插入意向锁的兼容性规则

当前锁类型 \ 请求锁类型插入意向锁间隙锁(Gap Lock)临键锁(Next-Key Lock)
插入意向锁✅ 兼容❌ 不兼容❌ 不兼容
间隙锁(Gap Lock)❌ 不兼容✅ 兼容✅ 兼容
临键锁(Next-Key)❌ 不兼容✅ 兼容❌ 不兼容
  • 关键规则:
    • 插入意向锁之间兼容:允许多个事务在同一间隙插入不同数据(如id=12和id=15)。
    • 插入意向锁与间隙锁互斥:若间隙已被其他事务加间隙锁,插入操作需等待。

4.4.4 插入意向锁的底层实现

  • 锁定目标:插入意向锁仅锁定目标插入点所在的间隙,而非整个范围。
  • 冲突检测:
    • 若插入位置已被其他事务的插入意向锁占用(如唯一键冲突),则触发等待。
    • 若插入位置未被占用,直接插入并释放锁。

示例:

sql
代码解读
复制代码
-- 表数据:id=10, 20(主键) -- 事务A BEGIN; INSERT INTO users (id) VALUES (15); -- 对间隙(10,20)加插入意向锁 -- 事务B BEGIN; INSERT INTO users (id) VALUES (18); -- 同样对间隙(10,20)加插入意向锁,允许执行

4.4.5 插入意向锁如何避免死锁

  • 场景:多个事务尝试插入同一间隙的不同位置。
  • 机制:
    • 插入意向锁之间兼容,事务不会互相阻塞。
    • 若插入位置相同(如唯一键冲突),后插入的事务会等待。

示例:

sql
代码解读
复制代码
-- 事务A和事务B同时插入不同位置 -- 事务A BEGIN; INSERT INTO users (id) VALUES (15); -- 成功 -- 事务B BEGIN; INSERT INTO users (id) VALUES (18); -- 成功 -- 结果:无死锁,两者均插入成功。

4.4.6 插入意向锁的监控与诊断

  1. 查看当前插入意向锁

    sql
    代码解读
    复制代码
    -- 查看InnoDB锁信息(MySQL 5.7+) SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS WHERE LOCK_MODE = 'X,INSERT_INTENTION'; -- 插入意向锁模式 -- 输出示例: -- LOCK_ID: 1234:5:3:2 -- LOCK_TYPE: RECORD -- LOCK_MODE: X,INSERT_INTENTION -- LOCK_DATA: 15 -- 插入目标值
  2. 分析插入意向锁冲突

    sql
    代码解读
    复制代码
    -- 查看被阻塞的插入操作 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
  3. 死锁日志分析

    sql
    代码解读
    复制代码
    SHOW ENGINE INNODB STATUS;

    在日志的LATEST DETECTED DEADLOCK部分,可找到插入意向锁导致的死锁信息。


4.4.7 插入意向锁的示例场景

  • 场景1:插入意向锁与间隙锁冲突

    sql
    代码解读
    复制代码
    -- 事务A BEGIN; SELECT * FROM users WHERE id > 10 FOR UPDATE; -- 加临键锁(锁定(10, +∞)) -- 事务B BEGIN; INSERT INTO users (id) VALUES (15); -- 需要插入意向锁,被事务A阻塞
  • 场景2:插入意向锁之间的兼容

    sql
    代码解读
    复制代码
    -- 事务A BEGIN; INSERT INTO users (id) VALUES (15); -- 加插入意向锁(间隙(10,20)) -- 事务B BEGIN; INSERT INTO users (id) VALUES (18); -- 加插入意向锁(同一间隙,兼容)

4.4.8 总结

  • 插入意向锁是间隙锁的优化:允许并发插入同一间隙的不同位置,提升写入性能。
  • 冲突机制:与普通间隙锁互斥,但插入意向锁之间兼容。
  • 优化核心:分散插入热点、减少事务时间、合理设计索引。

五,总结

5.1 全局锁(Global Lock)

  • 锁定粒度:整个数据库实例。
  • 核心作用:确保备份时数据一致性。
  • 实现方式:
    • FLUSH TABLES WITH READ LOCK(FTWRL):阻塞所有写操作。
    • InnoDB引擎可使用mysqldump --single-transaction实现无锁热备份。
  • 适用场景:
    • 全库逻辑备份(非事务引擎表需手动加锁)。
  • 优缺点:
    • 优点:数据一致性高。
    • 缺点:阻塞所有写操作,影响业务可用性。

5.2 表级锁(Table-Level Lock)

  • 锁定粒度:整张表。
  • 核心作用:简单高效地控制表级并发。
  • 类型:
    1. 表共享读锁(S Lock):允许多个读操作,阻塞所有写操作。
    2. 表独占写锁(X Lock):完全独占,阻塞所有读写操作。
    3. 元数据锁(MDL):隐式保护表结构变更(DDL操作)。
    4. 意向锁(IS/IX):协调行锁与表锁的共存。
  • 适用场景:
    • MyISAM引擎默认使用(读多写少场景)。
    • InnoDB在无索引的全表操作时退化为表锁。
  • 优缺点:
    • 优点:开销小,加锁快,适合低并发场景。
    • 缺点:并发性能差,锁冲突概率高。

5.3 行级锁(Row-Level Lock)

  • 锁定粒度:单行或多行数据。
  • 核心作用:实现高并发下的细粒度控制。
  • 类型:
    1. 记录锁(Record Lock):锁定单行。
    2. 间隙锁(Gap Lock):锁定索引记录间的间隙。
    3. 临键锁(Next-Key Lock):记录锁 + 间隙锁(InnoDB默认)。
    4. 插入意向锁(Insert Intention Lock):优化并发插入。
  • 适用场景:
    • InnoDB引擎默认支持,适合高并发写入。
    • 需精确控制数据访问的事务场景。
  • 优缺点:
    • 优点:并发度高,锁冲突少。
    • 缺点:开销大,可能引发死锁。
注:本文转载自juejin.cn的normaling的文章"https://juejin.cn/post/7494635102174445603"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

133
开发工具
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top