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

从区间锁到行锁:一次高并发写入死锁治理实战

资源账户写入链路长期存在 MySQL 死锁报警日常量级达到 99。虽然业务层依赖消息总线重试后多数请求可以成功但从数据库和链路治理角度看这类问题已经属于稳定存在的并发设计缺陷。本次治理聚焦一个典型场景同一 uid 并发发放多个新资源且对应账户记录尚未创建。原始实现中select ... for update 与 insert ... on duplicate key update 的组合在记录不存在时触发 gap lock / next-key lock导致后续插入互相等待最终形成死锁。优化目标不是简单去掉 for update而是在保留数据准确性的前提下缩小锁范围。最终方案分两步• 在正式加锁前先尝试插入一条数量为 0 的初始化记录将“锁区间”收敛为“锁行”。• 废弃 on duplicate key update 的写法改为显式 insert在唯一键冲突时回退到 update把高风险死锁降级为可控的并发竞争。图 1MySQL 死锁治理与并发优化概念封面一、问题背景与影响问题发生在资源账户表 user_resource_account 的写入路径中。业务动作很直接用户获得某个资源后需要把对应数量累加到账户表。线上主要出现以下并发场景• 同一个 uid• 极短时间内并发发放多个不同的 resourceId• 这些 (uid, resourceId) 记录在账户表中尚不存在在这种情况下应用日志和数据库侧会频繁出现死锁报警。由于上层 bus 具备重试机制最终业务结果大多可恢复因此问题一度没有直接暴露为用户故障。但从工程视角看这类死锁仍然需要治理原因主要有 3 点• 数据库需要反复做无效锁竞争增加了事务回滚和重试成本。• 消息链路承担了额外的消费抖动放大了热点用户或热点活动下的链路不稳定性。• 高频死锁会稀释异常告警的信噪比不利于真正高风险故障的识别。因此这次治理的目标不是“让重试成功率更高”而是把这条写入路径从“依赖重试兜底”改造成“并发行为本身更稳定”。二、现网实现与问题入口原始实现的核心 SQL 如下SQLselect *from user_resource_accountwhere uid #{uid} and resource_id #{resourceId}for update;SQLinsert into user_resource_accountset uid #{uid},resource_id #{resourceId},free_num #{num},total_num #{num}on duplicate key updatefree_num free_num #{num},total_num total_num #{num};如果只看业务语义这套实现很自然• 通过 select ... for update 保证并发安全。• 如果账户记录不存在则通过 insert 建立新记录。• 如果多个线程并发命中同一唯一键则依赖 on duplicate key update 完成累加。问题在于这段逻辑在“记录已经存在”的情况下通常没有问题而真正的高风险点恰恰出现在“记录不存在”的分支。三、死锁形成机制3.1 关键事实记录不存在时锁的不一定是空这次问题的关键认知是select ... for update 在目标记录不存在时并不是“什么都不锁”。在 InnoDB 的可重复读锁模型下如果查询命中的是唯一索引上的一个不存在的键值数据库可能会在相邻索引区间上加 gap lock 或 next-key lock以阻止其他事务在该区间插入数据。这意味着业务虽然只是“查不到一条记录”但数据库实际保护的是“一段索引范围”。3.2 死锁序列还原假设同一个用户 U 并发发放两个新资源 ResourceA 和 ResourceB且两条账户记录都不存在。一个可行的死锁序列如下• 事务 T1 执行 select ... where uid U and resource_id ResourceA for update• 事务 T2 执行 select ... where uid U and resource_id ResourceB for update• 由于两条记录都不存在T1 与 T2 分别在相邻索引区间上持有 gap lock / next-key lock• T1 继续执行 insert ResourceA• T2 继续执行 insert ResourceB• 插入阶段需要申请插入意向锁而该锁与对方之前持有的区间锁冲突• 最终形成循环等待MySQL 选择回滚其中一个事务图 2死锁形成与优化后的概念对比示意图3.3 为什么不是简单的唯一键冲突如果是两个线程并发写同一个 (uid, resourceId)更常见的现象通常是• 一个线程插入成功• 另一个线程抛出 DuplicateKeyException而本次线上实际出现的是 Deadlock found when trying to get lock。这说明事务失败点发生在“拿锁阶段”而不是“唯一键校验阶段”。因此问题本质不是主键或唯一键冲突而是锁顺序与锁粒度导致的互相等待。四、方案设计约束在确定优化方案前先明确几个边界条件。第一不能简单取消 for update。 这条链路对应的是资源账户数量后续需要支持账务核对和问题追踪因此写入过程仍然需要明确的并发控制。第二不能只依赖重试来消化问题。 重试解决的是结果可恢复不解决数据库层的高频锁竞争。第三优化目标不是“完全无竞争”而是把冲突从死锁互等收敛成系统能够稳定处理的形式。基于这些约束优化方向应当是在保留数据准确性的前提下缩小锁范围、简化写入路径、降低锁竞争的不确定性。五、优化方案5.1 方案一预创建 0 值记录在正式执行 for update 之前先尝试插入一条数量为 0 的账户记录对于新资源相关的过期记录也执行同样的预创建逻辑。这一步的价值在于把原来的“查不到记录”转化为“查得到记录”。后续再执行 for update 时锁住的就是目标行本身而不是索引区间。从锁行为上看这个改动的核心收益是• 原先的热点竞争发生在“索引区间”• 改造后主要竞争收敛到“真实存在的记录”也就是说这一步并没有消除并发而是显著缩小了锁影响范围。5.2 方案二显式 insert冲突后回退 update第二个改动是废弃 on duplicate key update改成更明确的写入路径JAVAprivate ResourceUserAccount addResourceAccount(Long uid, Resource Resource, int num, Date currentTime) {try {ResourceUserAccountMapper.insertOldData(uid, Resource.getPlatform(), Resource.getresourceId(), num, currentTime);} catch (DuplicateKeyException e) {log.warn(ResourceUserAccount duplicate key on insert, fallback to update. uid:{}, resourceId:{},uid, Resource.getresourceId(), e);ResourceUserAccountMapper.updateNum(uid, Resource.getresourceId(), num, currentTime);} catch (Exception e) {log.error(addResourceAccountException, e);throw e;}return ResourceUserAccountMapper.getUserResourceInfo(uid, Resource.getresourceId());}采用这条路径后并发场景会变得更可解释• 如果记录尚未建立则插入成功。• 如果其他线程已经抢先插入则本线程拿到 DuplicateKeyException。• 捕获异常后再执行 update 完成数量累加。这相当于把原来耦合在一条 SQL 里的“插入或更新”语义拆开使冲突路径更透明也更便于监控和告警分类。六、方案收益与边界6.1 机制层面的收益这次改造带来的收益主要体现在机制上而不是语法层面• 把锁从区间锁收敛为行锁降低了“记录不存在”分支的锁放大问题。• 把异常从死锁回滚收敛为唯一键竞争降低了事务互等的复杂度。• 把写入路径从“数据库自行决定插入或更新”改成“应用明确控制回退逻辑”提升了问题定位与可观测性。6.2 边界与残余风险这个方案并不意味着该链路从此不会再有锁竞争。仍然需要注意几个边界• 如果后续在同一事务中继续引入更多表、更多索引或更复杂的锁顺序仍然可能产生新的死锁路径。• 如果同一 uid 的热点写入强度持续升高虽然死锁概率下降但行级竞争和重试成本仍然可能成为性能瓶颈。• 如果初始化逻辑和正式写入逻辑之间缺乏统一约束可能引入新的数据一致性分支。因此这个方案的正确解读不是“彻底消灭冲突”而是“把原来最难处理的冲突形态降级为更可控的竞争形态”。七、可复用经验结合这次治理可以沉淀出几条更通用的经验• 在高并发场景下最危险的往往不是更新已有记录而是并发创建原本不存在的记录。• select ... for update 不能只从业务语义理解还必须结合 InnoDB 的锁语义理解尤其要关注不存在记录时的区间锁行为。• on duplicate key update 适合简化普通写入逻辑但在热点写入、强一致或复杂事务场景下需要评估其锁行为是否可接受。• 处理并发问题时与其追求零冲突不如优先把冲突变成可预测、可恢复、可观测的形态。八、总结这次资源账户死锁治理本质上不是一次 SQL 改写而是一次对锁模型的纠偏。业务层看到的是“账户不存在先查再插”数据库层看到的却可能是“多个事务正在竞争同一段索引区间”。只要这个认知偏差不被修正类似的问题就会在库存、额度、账户、券包等场景反复出现。这次优化的价值归根到底是两点• 让锁落在真实存在的记录上而不是落在不确定的区间上。• 让冲突以应用能够明确处理的方式暴露出来而不是以事务互锁的方式随机失败。这也是后续处理类似并发写入问题时一个值得优先复用的思路。

相关文章:

从区间锁到行锁:一次高并发写入死锁治理实战

资源账户写入链路长期存在 MySQL 死锁报警,日常量级达到 99。虽然业务层依赖消息总线重试后多数请求可以成功,但从数据库和链路治理角度看,这类问题已经属于稳定存在的并发设计缺陷。 本次治理聚焦一个典型场景:同一 uid 并发发放…...

3步掌握Textractor:游戏文本提取神器,让外语游戏无障碍畅玩

3步掌握Textractor:游戏文本提取神器,让外语游戏无障碍畅玩 【免费下载链接】Textractor Extracts text from video games and visual novels. Highly extensible. 项目地址: https://gitcode.com/gh_mirrors/te/Textractor 还在为看不懂日语RPG的…...

Gramps家谱软件完整指南:如何轻松构建您的家族历史数据库

Gramps家谱软件完整指南:如何轻松构建您的家族历史数据库 【免费下载链接】gramps Source code for Gramps Genealogical program 项目地址: https://gitcode.com/gh_mirrors/gr/gramps 您是否曾为家族历史资料零散而烦恼?是否想系统整理祖辈故事…...

React Boilerplate接近检测与交互优化:构建现代化用户体验的终极指南

React Boilerplate接近检测与交互优化:构建现代化用户体验的终极指南 【免费下载链接】react-boilerplate 🔥 A highly scalable, offline-first foundation with the best developer experience and a focus on performance and best practices. 项目…...

如何快速掌握WindowResizer:3分钟学会强制调整任意窗口大小的完整指南

如何快速掌握WindowResizer:3分钟学会强制调整任意窗口大小的完整指南 【免费下载链接】WindowResizer 一个可以强制调整应用程序窗口大小的工具 项目地址: https://gitcode.com/gh_mirrors/wi/WindowResizer 还在为那些顽固不化、无法调整大小的应用程序窗口…...

如何用The Super Tiny Compiler掌握作用域与符号表管理:完整指南

如何用The Super Tiny Compiler掌握作用域与符号表管理:完整指南 【免费下载链接】the-super-tiny-compiler :snowman: Possibly the smallest compiler ever 项目地址: https://gitcode.com/gh_mirrors/th/the-super-tiny-compiler The Super Tiny Compiler…...

如何用NSC_BUILDER轻松管理你的Switch游戏文件:3个实用技巧

如何用NSC_BUILDER轻松管理你的Switch游戏文件:3个实用技巧 【免费下载链接】NSC_BUILDER Nintendo Switch Cleaner and Builder. A batchfile, python and html script based in hacbuild and Nuts python libraries. Designed initially to erase titlerights enc…...

解放双手:MediaFire 批量下载神器,一键获取海量资源

解放双手:MediaFire 批量下载神器,一键获取海量资源 【免费下载链接】mediafire_bulk_downloader Script for bulk downloading entire mediafire folders for free using python. 项目地址: https://gitcode.com/gh_mirrors/me/mediafire_bulk_downlo…...

终极Python指南实战:数据一致性保证的完整解决方案

终极Python指南实战:数据一致性保证的完整解决方案 【免费下载链接】python-guide Python best practices guidebook, written for humans. 项目地址: https://gitcode.com/gh_mirrors/py/python-guide Python指南(python-guide)是一…...

OpCore Simplify:告别黑苹果配置难题,四步构建完美EFI

OpCore Simplify:告别黑苹果配置难题,四步构建完美EFI 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 还在为黑苹果复杂的Open…...

Boomi将在2026年Boomi World大会上发布数据激活和AI驱动创新的未来规划

这场全球顶级盛会将重点展示各类组织如何激活数据,为AI到商业智能的各类应用提供支持 数据激活公司Boomi今日宣布举办2026年Boomi World大会,这是该公司一年一度的顶级用户大会,将于2026年5月11日至14日在伊利诺伊州芝加哥举行。本次大会将汇…...

终极指南:os-tutorial引导加载器与二级引导程序深度解析

终极指南:os-tutorial引导加载器与二级引导程序深度解析 【免费下载链接】os-tutorial How to create an OS from scratch 项目地址: https://gitcode.com/gh_mirrors/os/os-tutorial os-tutorial是一个从零开始创建操作系统的开源项目,本文将深入…...

终极免费开源卡拉OK游戏:UltraStar Deluxe完全指南

终极免费开源卡拉OK游戏:UltraStar Deluxe完全指南 【免费下载链接】USDX The free and open source karaoke singing game UltraStar Deluxe, inspired by Sony SingStar™ 项目地址: https://gitcode.com/gh_mirrors/us/USDX 你是否渴望在家就能享受专业KT…...

Bilibili评论爬虫:轻松获取完整B站评论数据的终极解决方案

Bilibili评论爬虫:轻松获取完整B站评论数据的终极解决方案 【免费下载链接】BilibiliCommentScraper B站视频评论爬虫 Bilibili完整爬取评论数据,包括一级评论、二级评论、昵称、用户ID、发布时间、点赞数 项目地址: https://gitcode.com/gh_mirrors/b…...

Qwen3.5-2B效果对比展示:不同Temperature下代码生成稳定性与创造性实测

Qwen3.5-2B效果对比展示:不同Temperature下代码生成稳定性与创造性实测 1. 模型概览 Qwen3.5-2B是Qwen3.5系列中的轻量化多模态基础模型,仅20亿参数规模,专为低功耗、低门槛部署场景设计。该模型遵循Apache 2.0开源协议,支持免费…...

Pikachu靶场搭建与漏洞环境配置避坑指南:Windows 11 + PHPStudy 2024最新版

Pikachu靶场搭建与漏洞环境配置避坑指南:Windows 11 PHPStudy 2024最新版 在网络安全学习过程中,一个稳定、完整的漏洞靶场环境是实践的基础。Pikachu作为国内知名的Web漏洞练习平台,涵盖了从SQL注入到文件上传等常见漏洞类型。然而&#xf…...

ARMv9内存管理:TCR2寄存器详解与优化实践

1. ARMv9内存管理架构概述在ARMv9架构中,内存管理单元(MMU)作为处理器核心组件,负责虚拟地址到物理地址的转换。与ARMv8相比,ARMv9在内存管理方面引入了多项增强特性,其中最重要的变化之一就是新增了TCR2扩展寄存器系列。这些寄存…...

IDM激活脚本终极指南:一键实现永久免费使用

IDM激活脚本终极指南:一键实现永久免费使用 【免费下载链接】IDM-Activation-Script IDM Activation & Trail Reset Script 项目地址: https://gitcode.com/gh_mirrors/id/IDM-Activation-Script Internet Download Manager(IDM)作…...

保姆级教程:用Coze零代码搞定一个能聊天的微信公众号机器人(附服务器配置避坑指南)

零基础打造微信公众号智能助手:Coze平台全流程实战指南 在内容营销竞争白热化的今天,公众号运营者面临两大痛点:一是用户互动需求日益精细化,二是人力客服成本居高不下。据行业数据显示,接入智能对话系统的公众号用户留…...

三步解决Windows 11臃肿问题:Win11Debloat终极优化指南

三步解决Windows 11臃肿问题:Win11Debloat终极优化指南 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutter and c…...

D2L.ai SageMaker实战:Amazon机器学习平台终极指南

D2L.ai SageMaker实战:Amazon机器学习平台终极指南 【免费下载链接】d2l-en Interactive deep learning book with multi-framework code, math, and discussions. Adopted at 500 universities from 70 countries including Stanford, MIT, Harvard, and Cambridge…...

【AI 项目 Python 】文档与日志规范:从代码注释到生产追踪的工程实践

文章目录AI 项目 Python 文档与日志规范:从代码注释到生产追踪的工程实践一、引言二、Docstring 规范:代码即文档2.1 三种主流风格对比2.2 Google Style 完整示例2.3 类型注解规范三、项目级文档体系3.1 AI 项目推荐目录结构3.2 README 最小必要元素模型…...

物业师傅的实战笔记:一次搞定IC卡梯控延期,从读卡器选型到数据修改全流程避坑

物业工程师的IC卡梯控延期实战手册:从设备选型到数据修改全流程解析 刚接手小区物业维修工作时,最让我头疼的就是IC卡梯控系统。业主卡片一过期,电话就接个不停,而不同品牌的电梯控制器数据格式千差万别。经过三年实操&#xff0c…...

2048游戏AI助手:三步掌握数字合并的终极策略

2048游戏AI助手:三步掌握数字合并的终极策略 【免费下载链接】2048-ai AI for the 2048 game 项目地址: https://gitcode.com/gh_mirrors/20/2048-ai 你是否曾在2048游戏中屡屡失败,眼看就要合成大数字却功亏一篑?现在,一款…...

如何通过3步迁移完成R语言空间数据处理技术栈的终极升级

如何通过3步迁移完成R语言空间数据处理技术栈的终极升级 【免费下载链接】sf Simple Features for R 项目地址: https://gitcode.com/gh_mirrors/sf/sf 在R语言空间数据分析领域,从传统sp包迁移到现代sf包已成为技术演进的必然选择。sf包作为Simple Features…...

C++27范围库四大革命性扩展曝光:filter_view增强、zip_transform_v3、lazy_split_by、borrowed_range优化——你漏掉的性能跃迁机会在哪?

更多请点击: https://intelliparadigm.com 第一章:C27范围库扩展全景概览 C27 将对 头文件进行实质性增强,聚焦于提升范围组合的表达力、执行效率与编译期可推导性。标准委员会已正式采纳多项提案(P2954R0、P2976R1、P3026R0&am…...

终极指南:Reveal.js HTML演示框架从入门到精通

终极指南:Reveal.js HTML演示框架从入门到精通 【免费下载链接】reveal.js The HTML Presentation Framework 项目地址: https://gitcode.com/gh_mirrors/re/reveal.js Reveal.js是一款功能强大的HTML演示框架,让你轻松创建专业级演示文稿。无论你…...

零代码打造AI健身私教:MediaPipe创业项目落地终极指南

零代码打造AI健身私教:MediaPipe创业项目落地终极指南 【免费下载链接】mediapipe Cross-platform, customizable ML solutions for live and streaming media. 项目地址: https://gitcode.com/GitHub_Trending/med/mediapipe MediaPipe是一款跨平台、可定制…...

Wan2.1-umt5技术解析:深入理解其卷积神经网络优化策略

Wan2.1-umt5技术解析:深入理解其卷积神经网络优化策略 最近在社区里看到不少关于Wan2.1-umt5模型的讨论,大家普遍觉得它在处理文本和跨模态任务时,速度和效果都挺不错。作为一个长期关注模型底层优化的工程师,我很好奇它到底做了…...

终极指南:Immutable.js文档站的Next.js静态生成架构解析

终极指南:Immutable.js文档站的Next.js静态生成架构解析 【免费下载链接】immutable-js Immutable persistent data collections for Javascript which increase efficiency and simplicity. 项目地址: https://gitcode.com/gh_mirrors/im/immutable-js Immu…...