当前位置: 首页 > news >正文

【MySQL】锁机制

目录

  • 前情提要
    • 什么是事务?
    • 为什么需要锁?
  • 锁的分类
    • 按锁的粒度分类
      • 全局锁(Global Lock)
      • 表级锁(Table-Level Lock)
      • 行级锁(Row-Level Lock)
      • 页级锁(Page-Level Lock)
    • 按锁的类型分类
      • 共享锁(S Lock)
      • 排他锁(X Lock)
      • 意向锁(Intention Lock)
    • 按锁的实现方式分类
      • 悲观锁
      • 乐观锁
  • InnoDB 行级锁的四种实现
    • 记录锁(Record Lock)
    • 间隙锁(Gap Lock)
    • 临键锁(Next-Key Lock)
    • 插入意向锁(Insert Intention Lock)
  • 事务隔离级别与锁的关系
  • 锁的监控与死锁处理
    • 查看锁状态
    • 死锁自动处理
    • 避免死锁的黄金法则
  • 常见问题
    • 为什么我的 SQL 执行卡住?
    • 操作案例
  • 总结

前情提要

什么是事务?

定义:事务是一组必须全部成功或全部失败的数据库操作(例如:转账需要同时完成扣款和收款)

ACID 特性

  • 原子性(Atomicity):事务要么全部执行,要么全部回滚
  • 一致性(Consistency):事务执行后数据库必须处于合法状态(如账户余额不能为负数)
  • 隔离性(Isolation):事务之间互相隔离,通过锁机制实现
  • 持久性(Durability):事务提交后数据永久保存

为什么需要锁?

典型问题:两个用户同时购买最后一个商品,无锁会导致超卖

锁的作用:像“单线程”操作关键数据,防止并发冲突

锁的分类

按锁的粒度分类

全局锁(Global Lock)

定义:锁定整个 MySQL 实例,所有数据库的写操作会被阻塞。

使用场景:全库逻辑备份(如 mysqldump)。

语法

FLUSH TABLES WITH READ LOCK;  -- 加全局读锁
UNLOCK TABLES;               -- 释放锁

注意事项

  • 在 MySQL 里并没有像全局读锁 FLUSH TABLES WITH READ LOCK 那样专门用于全局写操作的全局写锁语句

  • 全局锁会导致业务停摆,备份推荐用 mysqldump --single-transaction(仅限 InnoDB)

表级锁(Table-Level Lock)

定义:锁定整张表,分为读锁(共享锁)和写锁(排他锁)

使用场景

  • MyISAM 引擎默认锁机制
  • 修改表结构(如 ALTER TABLE

语法

LOCK TABLES table_name READ;   -- 加读锁
LOCK TABLES table_name WRITE;  -- 加写锁
UNLOCK TABLES;                 -- 释放锁

代码示例

-- 会话A
LOCK TABLES orders READ; -- 会话A给orders表加了读锁
-- 会话B
INSERT INTO orders VALUES (...); -- 会话B给orders表添加数据被阻塞

行级锁(Row-Level Lock)

定义:锁定单行或多行数据(InnoDB 核心特性)

使用场景:高并发下精确控制数据访问

语法

SELECT * FROM users WHERE id=1 FOR UPDATE; -- 排他锁
SELECT * FROM users WHERE id=1 FOR SHARE;  -- 共享锁

注意事项

  • 行锁必须通过索引实现,否则退化为表锁
  • 示例:UPDATE users SET age=20 WHERE name='张三'(若 name 无索引,锁整个表)

页级锁(Page-Level Lock)

定义:锁定数据页(16KB),仅 BDB 引擎支持

现状:已被淘汰,了解即可

按锁的类型分类

共享锁(S Lock)

定义:允许多事务读同一数据,阻止写操作

类比:多人同时阅读同一本书,但不能修改

兼容性:与共享锁兼容,与排他锁冲突

排他锁(X Lock)

定义:独占锁,阻止其他事务读写

类比:你独占了书桌写作业,其他人不能看也不能改

兼容性:与所有锁冲突

意向锁(Intention Lock)

定义:表级锁,快速判断表中是否有行锁

  • IS 锁:事务计划加行级共享锁
  • IX 锁:事务计划加行级排他锁

作用:避免逐行检查锁状态,提升效率

按锁的实现方式分类

悲观锁

定义:假设并发冲突会发生,先加锁再操作

实现SELECT ... FOR UPDATE

适用场景:写操作频繁(如秒杀)

乐观锁

定义:假设无冲突,提交时检查版本号

实现

UPDATE products 
SET stock=stock-1, version=version+1 
WHERE id=100 AND version=current_version;

适用场景:读多写少(如文章阅读量统计)

InnoDB 行级锁的四种实现

记录锁(Record Lock)

定义:锁定索引中的一条记录

触发条件:精确匹配唯一索引(如 WHERE id=1)

代码示例

-- 锁定 id=1 的行
SELECT * FROM users WHERE id=1 FOR UPDATE;

间隙锁(Gap Lock)

定义:锁定索引记录之间的间隙(如 id 在 (5,10) 之间)

触发条件:范围查询或查询不存在的值

作用:防止其他事务插入数据(解决幻读)

仅生效于REPEATABLE READ 隔离级别

临键锁(Next-Key Lock)

定义:记录锁 + 间隙锁,锁定左开右闭区间

触发条件:非唯一索引的范围查询

代码示例

-- 锁定 age 在 (20,25] 区间
SELECT * FROM users WHERE age > 20 AND age <=25 FOR UPDATE;

插入意向锁(Insert Intention Lock)

定义:插入前标记目标位置的间隙锁

特点:不阻塞其他插入意向锁,但阻塞间隙锁

事务隔离级别与锁的关系

隔离级别脏读不可重复读幻读锁机制特点
Read Uncommitted可能可能可能不加锁,直接读最新数据(可能脏读)
Read Committed避免可能可能行锁,无间隙锁
Repeatable Read避免避免可能默认使用临键锁,防止幻读
Serializable避免避免避免所有 SELECT 自动转为 FOR SHARE

建议:默认使用 REPEATABLE READ,高并发写入场景可尝试 READ COMMITTED

锁的监控与死锁处理

查看锁状态

代码示例

-- 当前持有的锁
SELECT * FROM information_schema.INNODB_LOCKS;

-- 锁等待关系
SELECT * FROM information_schema.INNODB_LOCK_WAITS;

-- 事务状态(包含锁信息)
SELECT * FROM information_schema.INNODB_TRX;

死锁自动处理

InnoDB 自动检测死锁,回滚代价较小的事务

手动处理

SHOW ENGINE INNODB STATUS;  -- 查看死锁日志
KILL [transaction_id];      -- 终止事务

避免死锁的黄金法则

按固定顺序访问资源(如按主键排序更新)

尽量使用短事务,快速提交

为高频查询字段添加索引

常见问题

为什么我的 SQL 执行卡住?

可能原因:锁等待超时

解决步骤

  1. 查询阻塞事务:

    SELECT * FROM information_schema.INNODB_TRX;
    
  2. 终止阻塞者:

    KILL 12345; -- 替换为实际事务ID
    

操作案例

索引缺失导致锁升级

  1. 创建无索引表

    CREATE TABLE test (name VARCHAR(20));
    INSERT INTO test VALUES ('Alice'), ('Bob');
    
  2. 会话A

    BEGIN;
    UPDATE test SET name='Charlie' WHERE name='Alice'; -- 无索引,触发表锁
    
  3. 会话B

    UPDATE test SET name='Dave' WHERE name='Bob'; -- 被阻塞!
    

结论:无索引的更新操作会锁住整张表

总结

  1. 索引优化

    • 所有 WHERE 条件字段尽量加索引
    • 避免全表扫描(如 LIKE '%xxx%'
  2. 事务设计

    • 事务尽量简短,避免在事务中执行网络请求
    • 批量操作分批次提交
  3. 锁超时设置

    SET innodb_lock_wait_timeout = 5; -- 等待5秒自动放弃
    
  4. 锁替代方案

    • 乐观锁:通过版本号控制
    • 队列机制:异步处理高并发请求

相关文章:

  • DeepSeek技术架构解析:MoE混合专家模型
  • C语言-桥接模式详解与实践
  • 清华大学大模型智能体自我认知与决策流程!自知、反思、规划:城市环境目标导航中的大模型智能体新范式
  • 驱动开发的引入
  • k8s中运行nginx的亲和性
  • BigEvent项目后端学习笔记(二)文章分类模块 | 文章分类增删改查全流程解析(含优化)
  • 3.milvus索引-HNSW
  • SpringBoot集成Flyway
  • java八股文之并发编程
  • JSON Web Token (JWT) 完整指南
  • 决策树调参技巧
  • 数据库操作练习
  • 计算机网络——物理层设备
  • 深度剖析HTTP协议—GET/PUT请求方法的使用-构造请求的方法
  • Unity-AI-Deepseek生成的生成模型代码
  • 内存取证之windows-Volatility 3
  • SQL Optimization
  • uboot 编译过程
  • 蓝桥杯贪心
  • js逆向之断点调试和python补环境
  • 奥运“四朝元老”华天回国参赛,伤势未愈谨慎出战全国锦标赛
  • 齐白石精品在波士顿展出,“白石画屋”呈现水墨挥洒
  • 30平米的无障碍酒吧里,我们将偏见折叠又摊开
  • 汤加附近海域发生6.4级地震
  • 优化营商环境,服务上海“五个中心”建设,北外滩有何举措?
  • 时隔近4年再出征!长三丙成功发射通信技术试验卫星十九号