10个线程,同一张临时表,同一时刻写入。跑完一看,数据已经烂了——而且没有任何报错。这不是极端假设,是一套并发验证脚本要复现的失败模式。
![]()
问题出在表名上。多个批处理worker或微任务并发访问共享数据库时,如果临时表名是硬编码的、大家共用的,命名空间碰撞和竞态条件就会同时发生。写入被覆盖,读取拿到脏数据,甚至一个线程把另一个线程还在用的表直接DROP掉。
验证脚本把两种架构摆在一起对比。反模式这一侧,所有线程都往同一个wk_temp_table_shared_fixed写,共享同一个内存数据库URI(cache=shared)。结果是可以预期的:互斥失效、读偏斜、DROP级联、静默的数据损坏。
重构后的架构换了个思路。每个线程用任务令牌或UUID动态生成自己的表名,形如wk_temp_table_{task_id},再配上独立的数据库命名空间(cache=private)。碰撞在设计层面就被消除了,每个线程拿到的是私有缓存,执行结果确定。
为了让这套基准能进CI/CD流水线,脚本选用orjson做高吞吐的JSON序列化,把结构化验证指标直接打到标准输出。
但脚本第一次跑就挂了。不是并发逻辑的问题,是一行import。
执行时立刻抛出ModuleNotFoundError,卡在import orjson这一行。运行容器是个轻量沙箱环境,Python的site-packages里根本没装orjson。
QA复盘挖出三个疏漏。第一,这行import写在模块顶层,没有任何保护。第二,选了C实现的高性能序列化库本身没问题,但没留到标准库json的降级路径,等于把可移植性押在一个非标准包上。第三,没有requirements.txt,没有pyproject.toml,也没有自动引导流程,脚本对运行环境的假设完全落空。
一个用来验证别人并发问题的工具,自己先成了单点故障。
修复方向很直接:把import包进try/except,拿不到orjson就退回json,输出格式保持一致。诊断类工具本该尽量只用标准库原语,或者在核心执行路径之前做严格的依赖预检。
并发这一侧的原则同样清楚。硬编码的表标识符就是共享可变全局状态,应用层的互斥锁一旦横向扩到多节点或多容器就失效。动态表名把进程ID、UUID或分布式关联令牌嵌进去,在DDL和DML边界上保证隔离。更彻底的做法是交给数据库引擎管生命周期——SQLite的CREATE TEMP TABLE、PostgreSQL的ON COMMIT DROP、Snowflake的transient表,对象天然隔离在客户端连接内。
两件事,一个教训:并发隔离要靠命名策略,工具可靠性要靠依赖兜底。少一样,验证脚本和生产流水线都会在同一个地方翻车。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.