MySQL 事务隔离级别与锁定读的组合方案
MySQL 事务隔离级别与锁定读的组合方案
InnoDB 中,隔离级别决定读视图和部分锁策略,锁定读则用于保护随后要依赖或修改的数据。按已存在主键读取库存再扣减,可以在 READ COMMITTED 或 REPEATABLE READ 下使用同一事务中的 FOR UPDATE;只把隔离级别调高,并不能替代清晰的事务边界、索引条件和错误处理。
官方地址:https://dev.mysql.com/doc/refman/8.4/en/。本文以 MySQL 8.4 的 InnoDB 语义为范围,示例使用独立演示库。
先确定要保护哪项库存不变量
假设一个SKU只有一条库存记录,每次预留数量必须为正,成功后可用库存不能为负。这里保护的是单行数量变化,不包括订单创建、跨仓调拨和支付。先把目标缩小,才能判断需要普通读取、共享锁还是排他锁。
如果只是查询页面显示数量,普通SELECT通常更符合读取目的;如果必须先依据数量作决定,再修改同一行,就应考虑FOR UPDATE。若唯一任务是“库存足够就减掉数量”,带数量条件的单条UPDATE也能表达该不变量,不必为了形式完整额外读一次。本文保留锁定读,是为解释读后决策场景。
准备 InnoDB 与主键访问条件
下面的表用主键精确定位SKU。先在独立演示库准备表和虚构数据,再创建过程。DDL与库存预留不放进同一个业务事务,避免混淆事务控制。
-- 在独立演示库中准备表;DDL不放进业务事务。
CREATE TABLE stock_demo (
sku_id BIGINT PRIMARY KEY,
available INT NOT NULL,
CONSTRAINT ck_stock_demo_nonnegative CHECK (available >= 0)
) ENGINE = InnoDB;
-- 使用虚构SKU,避免操作真实库存。
INSERT INTO stock_demo (sku_id, available) VALUES (701, 20);
示例讨论的是已经存在的主键记录。若查询范围、联合索引条件或不存在记录发生变化,锁的覆盖也需要重新判断;不能根据WHERE里写了一个等号就认定永远只锁一条记录。

按读写目标组合隔离级别
| 组合 | 适合讨论的目标 | 需要注意 |
|---|---|---|
| READ COMMITTED与普通SELECT | 按语句读取已提交数据 | 同一事务多次读取可能不同 |
| REPEATABLE READ与普通SELECT | 需要一致读视图的读取 | 读视图不是未来修改的排他权 |
| READ COMMITTED与FOR UPDATE | 锁住当前要修改的记录 | 不能依赖它阻止一般范围内的新行插入 |
| REPEATABLE READ与FOR UPDATE | 结合当前锁定读与相应索引锁策略 | 范围扫描和唯一精确命中的锁覆盖不同 |
| FOR SHARE | 保护读取所依赖的记录 | 读后还要升级写锁时可能产生竞争 |
官方 隔离级别说明区分了两种一致读视图,也说明RC通常不使用间隙锁,但外键和重复键检查有例外。RR的范围锁定不能套用到RC。锁定读使用的状态与RR早先建立的一致读视图也可能不同,不应混用两次查询的值作同一判断。
在一个事务里完成锁定与扣减
下面给出mysql命令行客户端可使用的存储过程示例。它拥有事务,调用时必须确保连接没有活动事务;不要从外层订单事务中调用,以免START TRANSACTION影响已有事务边界。过程只演示单SKU库存扣减,不是完整订单系统。
-- 以下DELIMITER是mysql客户端指令,不是应用驱动SQL。
DELIMITER //
CREATE PROCEDURE reserve_stock_demo(IN p_sku BIGINT, IN p_qty INT)
SQL SECURITY INVOKER
BEGIN
DECLARE v_available INT DEFAULT NULL;
-- SQL失败时结束整个事务,并把错误交还调用方。
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_available = NULL;
-- 本过程拥有事务,只能在无活动事务的连接上调用。
IF p_sku IS NULL OR p_qty IS NULL OR p_qty = p_qty;
IF ROW_COUNT() 1 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'reservation rejected';
END IF;
COMMIT;
END//
DELIMITER ;
-- 在无活动事务的同一连接上,为下一事务指定隔离级别。
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
CALL reserve_stock_demo(701, 2);
过程先检查输入,再开启事务。FOR UPDATE取得库存值,数量不足或SKU不存在时抛错;异常处理器回滚并重新抛出,成功路径提交。调用方只有收到成功响应才能确认本次预留结果。代码未在本文制作中执行,不包含实测锁等待或数据库截图。
要改成RR,可在无活动事务的连接上把SET TRANSACTION语句中的READ COMMITTED替换为REPEATABLE READ;其余单行逻辑不必因此改为普通SELECT。此处不使用SET SESSION,避免示例把隔离级别长期留在连接池会话上。应用接入时仍应让设置与事务在同一物理连接执行,并确认驱动的连接复用行为。

锁要等事务结束才释放,不要在持锁期间等待支付接口、人工确认或慢速外部请求。FOR UPDATE也不意味着普通一致读全部被阻塞,其他事务仍可能读取其读视图中的版本。具体规则见 锁定读文档。
把权限和错误处理放进事务边界
示例采用SQL SECURITY INVOKER,运行时使用调用者权限,不借过程定义者身份扩大访问范围。创建过程与调用过程使用的账号职责应分开。MySQL 8.4中FOR UPDATE需要SELECT以及UPDATE、DELETE、LOCK TABLES中的至少一种权限;本例本来就要UPDATE库存,不需要为了锁定读另外授予删除能力。
死锁与等待超时必须作为业务失败处理,不要在事务状态不明确时只重发最后一条UPDATE。该过程的SQL异常路径主动回滚;若应用选择重试,应建立新事务、限制次数并采用退避。连接中断或提交响应丢失时,调用方还可能无法判断提交是否生效,因此真实预留接口需要独立业务请求号与幂等记录。本例没有这张记录表,不能直接无条件重试扣减。
日志宜记录业务请求号、SKU标识、请求数量、错误类别和耗时,避免输出连接口令或完整客户数据。锁等待与失败计数有助于定位争用,但不能仅凭接口超时判断“事务一定没提交”。
接入前的检查与常见问题
在自己的演示环境中,可以用两个独立连接请求同一SKU,观察竞争是否遵守预期,并覆盖库存不足、缺失SKU、非法数量和错误回滚路径。接入时还应确认主键访问条件、事务是否过长、存储过程权限和客户端错误处理。这些是读者的接入建议,本文发布流程未运行数据库校验。
能否用FOR SHARE读取后再修改?可以出现锁升级竞争。明确准备修改同一行的业务,优先把排他锁意图写清;不能把共享锁当作没有代价的FOR UPDATE替代品。
SKIP LOCKED可以防止库存接口等待吗?它会排除已锁记录,可能把正在竞争的SKU表现为空结果。适用于允许跳过记录的队列等语义,不应直接替换普通库存查询。
选择RC就不再有死锁吗?不是。隔离级别调整不能取消所有锁竞争;多行业务仍需要一致的锁定顺序、短事务和有界重试。
MySQL 默认表达式与生成列的迁移清单
- 上一篇
- MySQL 默认表达式与生成列的迁移清单
- 下一篇
- MySQL Performance Schema 按线程聚合等待事件
-
- 数据库 · MySQL | 1小时前 |
- MySQL 不同字符集列的索引长度参数对比
- 118浏览 收藏
-
- 数据库 · MySQL | 1小时前 |
- MySQL Performance Schema 按线程聚合等待事件
- 306浏览 收藏
-
- 数据库 · MySQL | 1小时前 |
- MySQL 默认表达式与生成列的迁移清单
- 368浏览 收藏
-
- 数据库 · MySQL | 1小时前 |
- MySQL sys.schema_table_statistics_with_buffer 怎样定位表级 IO
- 335浏览 收藏
-
- 数据库 · MySQL | 1小时前 |
- MySQL INFORMATION_SCHEMA.TABLES 怎样区分表大小与索引大小
- 175浏览 收藏
-
- 数据库 · MySQL | 1小时前 |
- MySQL CHECK TABLE 怎样判断表结构与索引是否正常
- 374浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- MySQL 分区表的分区裁剪条件设计
- 154浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- MySQL LAG 与 LAST_VALUE 窗口帧的结果边界
- 432浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- MySQL 窗口函数共享排序的 WINDOW 子句写法
- 232浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- MySQL SHOW CREATE TABLE 和 INFORMATION_SCHEMA 为什么看到的属性不同
- 287浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- MySQL CHECKSUM TABLE 怎样比较两张表的数据摘要
- 233浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- MySQL 8.4 ANALYZE TABLE 直方图的桶数量配置方法
- 417浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 816次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 807次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 899次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 870次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 693次使用
-
- MySQL JSON_TABLE 展开数组时如何保留缺失字段
- 2026-09-10 501浏览
-
- MySQL 分区表怎么处理跨分区唯一键:分区列约束与建表取舍
- 2026-08-30 501浏览
-
- MySQL权限管理设置全攻略
- 2026-03-29 501浏览
-
- MySQL分片实现方法及常见方案解析
- 2025-06-24 501浏览
-
- MySQL表空间碎片怎么清理?超详细优化教程
- 2025-06-13 501浏览

