当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL 事务隔离级别与锁定读的组合方案

MySQL 事务隔离级别与锁定读的组合方案

来源:17golang原创 2026-10-31 23:22:41 0浏览 收藏

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就不再有死锁吗?不是。隔离级别调整不能取消所有锁竞争;多行业务仍需要一致的锁定顺序、短事务和有界重试。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
MySQL 默认表达式与生成列的迁移清单MySQL 默认表达式与生成列的迁移清单
上一篇
MySQL 默认表达式与生成列的迁移清单
MySQL Performance Schema 按线程聚合等待事件
下一篇
MySQL Performance Schema 按线程聚合等待事件
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    816次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    807次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    899次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    870次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    693次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码