Python 更新 JSON 配置时,怎样避免留下半份文件

举报
yd_271400591 发表于 2026/11/13 08:51:16 2026/11/13
【摘要】 用可运行的 Python 示例演示同目录临时文件和 os.replace,验证 JSON 序列化失败后旧配置仍可读,并说明并发更新与断电恢复的边界。

一个服务每隔几分钟读取 config.json。更新脚本以 w 模式打开文件再执行 json.dump(),此时旧文件已经被截断。若序列化到一半遇到不支持的数据类型,读配置的服务就可能看到无法解析的 JSON。即使写入成功,并发读取也可能刚好撞上写入过程。

可以先在同一个目录写临时文件,写完并关闭后,再用 os.replace() 更新目标路径。目标目录相同可以避免跨文件系统替换。下面的例子在临时目录中执行,既验证正常更新,也验证异常时原配置仍可读取。

import json
import os
from pathlib import Path
from tempfile import NamedTemporaryFile, TemporaryDirectory


def save_json(path, data):
    temp_path = None
    try:
        with NamedTemporaryFile(
            mode="w", encoding="utf-8", dir=path.parent,
            prefix=f".{path.name}.", suffix=".tmp", delete=False,
        ) as file:
            temp_path = Path(file.name)
            json.dump(data, file, ensure_ascii=False, allow_nan=False)
            file.write("\n")
            file.flush()
            os.fsync(file.fileno())

        os.replace(temp_path, path)
    finally:
        if temp_path is not None:
            temp_path.unlink(missing_ok=True)


with TemporaryDirectory() as folder:
    config = Path(folder) / "config.json"
    save_json(config, {"version": 1, "name": "备份任务"})

    try:
        save_json(config, {"version": 2, "unsupported": {1, 2}})
    except TypeError:
        print("序列化失败后", json.loads(config.read_text(encoding="utf-8")))

    save_json(config, {"version": 2, "name": "备份任务"})
    print("正常更新后", json.loads(config.read_text(encoding="utf-8")))
    print("残留临时文件", list(config.parent.glob("*.tmp")))
序列化失败后 {'version': 1, 'name': '备份任务'}
正常更新后 {'version': 2, 'name': '备份任务'}
残留临时文件 []

这个流程把可能失败的序列化和写入都放在临时文件上。成功替换时,按路径新打开文件的读者会读到完整文件。已经持有旧文件句柄的读者可能继续读取旧内容,具体取决于系统的文件共享方式。替换前关闭临时文件也有助于在 Windows 上避免自己持有的句柄阻碍替换。

flush() 先把 Python 的缓冲交给系统,fsync() 再请求同步文件数据。若需要覆盖突然断电的恢复要求,还要考虑目录项同步、存储系统和备份恢复。原子替换也没有解决多个写者的更新冲突。如果两个进程都从版本 1 出发修改配置,最后替换的进程仍可能覆盖另一个人的修改。这时可以把更新交给单一写进程,或者在读、改、写整个过程加锁。

替换的是文件本身,原文件的权限和所有权也需要按部署要求检查。最容易验证的起点是先在临时目录跑上面的正常和异常路径,再把同样的检查加入配置更新流程。

参考资料为 Python 标准库文档 os.replace()、os.fsync() 和 tempfile.NamedTemporaryFile()。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。