尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
两个进程同时写一个 JSON 队列文件之后:一次丢更新事故的复盘
自动化系统里任务队列常常用一个大 JSON 文件来存结构简单肉眼可读改起来也方便。但当不止一个进程要对它做读-改-写时这个看似无害的设计就会埋雷。本文复盘一次真实的丢更新事故两个工作进程并发写同一个 JSON 队列文件结果一条任务被投递了两次另一条不翼而飞。现象一条任务跑了两次另一条凭空消失监控先报的是重复执行同一条任务出现在两次执行记录里日志时间戳只差几秒而且是同一个任务 ID、同一份参数不是相似任务。起初以为是上游重复投递但顺着另一条线索看队列里有一个任务项没有对应任何执行记录——它凭空消失了。一边是重复一边是丢失两个症状同时出现在同一个队列文件上这不太像单纯的重复投递。排查从生成端日志到字节偏移比对排查走了一点弯路。先怀疑任务生成端重复投递。把生成侧的日志完整核对了一遍这条任务只生成过 1 次投递语句也只出现一次任务 ID 全局一致生成端排除。接着换思路不再问谁投递了它而是问文件里发生了什么。我们在每次写文件时顺手留了带时间戳的备份这也是后面能定位的关键把几个时间点的快照拉出来做版本号/字节偏移比对发现一个时间窗口非常可疑B 进程在 T0 时刻读文件、在内存里计算新队列、准备写回这个动作花了约 3 秒而 A 进程在这 3 秒的中途也读了一次文件并在 T02 秒左右先写回B 在 T03 秒才写回。B 写回的内容是基于 3 秒前的旧文件算出来的A 刚写进去的那个任务项被整体覆盖掉了。从字节偏移看更直观A 写回的文件比 B 写回的文件多出一段新增任务的 JSON 片段而这段片段在 B 的版本里根本不存在。两个进程都在正确地做事坏就坏在它们各自基于不同版本的文件计算然后整体覆盖。根因read-modify-write 不是原子操作复盘下来问题可以归结为三点读-改-写read-modify-write这个复合操作本身不是原子的。读文件、在内存里改、写回文件中间任何一个时刻都可能被别的进程插进来。没有任何文件锁。两个进程谁也不知道对方正在写写回时也不校验我读到的版本还是不是当前版本。两个进程地位对等没有主从之分。没有谁负责串行化写入也没有谁负责裁决冲突。后果就是经典的丢失更新lost updateA 和 B 同时基于旧版本计算后写的整体覆盖先写的。A 的写覆盖了 B 刚写入的任务于是这条任务被 A 再次投递而 B 在内存里改过、还没来得及写回确认的另一个队列项被覆盖之后再没有落盘——这就是重复 丢失同时出现的原因。值得强调的是两个进程全程没有抛出任何异常文件也始终是合法 JSON系统从外部看一切正常这正是这类事故难以察觉的原因。修复三条路线的对比与选择现场讨论了三种方案方案一每账号拆独立队列文件。约定每个账号对应自己的 queue_{account}.json谁有权限处理哪个账号就只写哪个文件。共享消失冲突自然消失。方案二文件锁。写前加锁写完释放Linux 下用 flock需要跨平台时可以用 lockfile 目录占位。方案三单写者进程加 IPC。只留一个进程负责写文件其他进程通过管道、Socket 或消息队列把变更发给它由它串行落盘。三条路线的取舍方案二保留了共享结构改动小但锁的粒度、锁超时、进程崩溃后锁残留这些新问题都要处理方案三结构上干净但要引入 IPC 通道和单点进程的存活管理对当时的系统来说改动偏大方案一不动锁、不加组件只是把一个队列传给多个写者改成一个队列至多一个写者改动集中在文件命名和任务认领逻辑上。另外拆分还带来一个附带收益单文件变小读写耗时下降出问题时快照也比一个巨型文件好查得多。落地时选了方案一并叠加一条纪律保证同一账号在任一时刻只有一个写者进程。示意代码# queue_shard.py —— 每账号独立队列 写者互斥 import json, fcntl, contextlib contextlib.contextmanager def account_queue(account): path fqueues/{account}.json lock_path fqueues/.{account}.lock with open(lock_path, w) as lf: fcntl.flock(lf, fcntl.LOCK_EX) # 同一账号的写者在此排队 with open(path, r, encodingutf-8) as f: yield f def push_task(account, task): with account_queue(account) as f: data json.load(f) data[tasks].append(task) data[version] 1 f.seek(0) json.dump(data, f, ensure_asciiFalse) f.truncate() # 覆盖旧内容防止残留即便拆了文件写回时仍要依赖锁的排他性别让读到的版本和写回的版本有机会错开。如果暂时不能拆文件flock 是兜底做法# 用 flock 把读-改-写整体锁成临界区 flock /var/lock/taskqueue.lock -c ./update_queue.sh教训丢的往往是别人的更新日志很难还原这次事故有个让人后怕的细节如果没有提前留下的文件快照事后仅凭业务日志几乎不可能还原真相——被丢的那个任务从未进入执行流程重复的那条也只是看起来像重复投递。并发写同一文件时丢的常常是别人的更新受害者甚至不知道自己丢了东西。所以除了结构上的修复还补了两件事一是写前快照每次写回前把当前文件另存一份带时间戳的副本保留若干天成本几乎为零二是审计行每次读-改-写记一条进程号 读到的版本号 写回的版本号 变更摘要追加到单独的审计文件里出问题时能拼出完整时间线。拆分之后每个账号的队列文件只被一个写者触碰审计行的作用从破案降级为巡检这正是想要的状态。并发问题不总是以报错的形式出现更多时候是安静地覆盖掉别人的写入。队列文件越简单越要问一句它有几个写者参考文章这次事故的修复思路里让写者互不打扰和让进程长期活着是两件配套的事。如果你也在维护一套长驻的自动化系统可以接着读这两篇Windows 定时任务与看门狗让自动化脚本活过重启微信 FAQ 自动回答把重复问题交给知识库
RELATED

相关推荐

状态扩展Dijkstra:处理带免费次数的最短路问题(ABC441 Paid Walk题解)

状态扩展Dijkstra:处理带免费次数的最短路问题(ABC441 Paid Walk题解)

周六晚上的ABC441,我栽在D题Paid Walk上了。题名一看就是图论,我第一版直接写了普通Dijkstra,样例跑得很顺,交上去却WA到怀疑人生。赛后补题才彻底想明白:这题真正要处理的是"费用计算依赖额外状态"这件事&a…

📅 2026/9/30 9:12:09
化工园区安全整治硬核逻辑:从风险辨识到HAZOP的20年实战

化工园区安全整治硬核逻辑:从风险辨识到HAZOP的20年实战

1. 安全整治的“前世今生”:从文件柜里的预案到一线人员的肌肉记忆 做化工园区安全这行,到今年正好满20年。说句掏心窝子的话,这个领域最不缺的就是红头文件和检查表格,但最缺的是两样东西:一是真正读过事故调查报告全…

📅 2026/9/30 9:12:09
RAG 效果差?先别急着换模型,90% 的问题出在检索层

RAG 效果差?先别急着换模型,90% 的问题出在检索层

RAG 效果差?先别急着换模型,90% 的问题出在检索层 获取方式 完整的项目课程、配套代码与文档资料我整理在 资源获取 上线了一个基于大模型的知识库问答,测试时用户问"退货政策是几天内可以退",模型洋洋洒洒答了一段——…

📅 2026/9/30 9:12:09
MORE NEWS

更多资讯

📰

支持向量机从原理到实战:SVM的Python实现与参数调优全解析

简介:一份面向机器学习入门者与Python开发者的支持向量机(SVM)Python实现资源,以精简可运行的代码和配套数据,直观展示SVM从数据到分类模型的学习过程。压缩包共6个文件,其中3个Python源码文件分别承担核心…

📰

ClickHouse JSON处理实战:从JSON类型到行列转换全解析

搞数据的人一定都有这种感受:业务方扔过来的数据,十个里有八个长着 JSON 的样。ClickHouse 又是出了名的强类型列存,两者一撞,第一个反应就是拿 String 硬存,再掏出 JSONExtract 系列函数一层层剥。我之前在项目里处理…

📰

视觉惯性组合导航从原理到实践:无人系统开发验证平台搭建指南

如果你最近在搞无人机、机器人或者车载平台,你应该会注意到一个趋势:以前大家做自主定位,首选RTK、激光雷达或者纯视觉方案,但现在越来越多的团队开始把“视觉 惯性”作为标配,也就是视觉惯性组合导航。我做这个方向也…

📰

jevgrep 解析黑科技:WASM 辅助 worker 如何高效提取 Python/TypeScript 声明与调用链

jevgrep 解析黑科技:WASM 辅助 worker 如何高效提取 Python/TypeScript 声明与调用链 【免费下载链接】jevgrep Find code by asking what it does. A CLI for coding agents that uses Jev to discover relevant files and source context. 项目地址: https://gi…

📰

华为Mate 90系列官宣10月1日发布开售!外观拼接设计,带外接摄影配件!

据华为官方消息,华为Mate 90系列年度旗舰手机已正式定档,将于10月1日上午10:00正式发布,并于同日12:08全渠道同步开售。华为终端官方宣布了上述信息,并同步公开了Mate 90 Pro Max典藏版手机的官方图片,新机回归双拼设计…

📰

UE5.8渲染调试指南:从光线、采样到性能优化实战

UE5.8 的渲染体系已经和过去完全不同了。几年前做项目还要手动摆放反射球、烘焙光照贴图、给模型做一堆LOD,现在打开引擎,Nanite承担几何体细节,Lumen负责动态全局光照,虚拟阴影贴图按需分配分辨率,TSR还能用低分辨率重…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬