JSON比pickle慢的根本原因是其在Python层逐字符执行重复的类型检查、Unicode编码和字符串拼接;而真正快的方案是启用strict_types的msgpack,因其跳过类型检查、直接C写二进制流。

JSON 比 pickle 慢,根本原因不是“文本 vs 二进制”,而是它被迫做大量重复的类型检查、Unicode 编码和字符串拼接——这些都在 Python 层逐字符执行。
JSON 序列化为什么在大型对象上特别卡
JSON 规范强制所有 key 和 string 值必须是 UTF-8 编码的 Unicode 字符串。这意味着:json.dumps() 对每个字符串都要调用 str.encode('utf-8'),对每个 dict key 都要验证是否为 str 类型,还要反复分配临时 buffer 拼接 JSON 字符串(如 "{" + '"id":' + "123" + "," + ...)。当对象含 50 万个字典时,这种开销会指数级放大。
- 遇到非 ASCII 字符(比如中文)时,
json会额外做 escape(\u4f60),而pickle直接写 bytes -
json不支持 tuple —— 自动转成 list,引发隐式拷贝 - 没有引用去重机制:相同子对象(如重复的
{"status": "ok"})会被序列化 N 次,而pickle只存一次并记录引用
pickle 实际比 JSON 快,但仍有明显瓶颈
pickle 虽然快于 json,但它慢在另一套逻辑:它必须遍历整个对象图,维护内部引用表,并为每个对象写入类型标识(如 b'\x80\x05' 表示 protocol 5)、引用 ID 和重建指令。尤其当对象含自定义类实例或循环引用时,pickle 会降级为安全但极慢的递归模式。
- 默认协议版本低:Python 3.8+ 默认用 protocol 4,但旧环境可能卡在 protocol 3,显式指定
pickle.HIGHEST_PROTOCOL能提速 15–20% - 自定义类没实现
__getstate__或含不可序列化属性(如threading.Lock),会触发 fallback 到纯 Python 回退路径 - 不建议对含
datetime、decimal等对象直接dumps—— 它们会走慢速 default handler,应预处理为int或str
真正快的方案:msgpack(C 扩展 + strict mode)
如果你的数据全是基础类型(dict、list、str、int、float、bytes、None),msgpack 是目前 Python 中最快的选择。它跳过所有类型检查,直接用 C 写二进制流,且默认启用 use_bin_type=True,避免 UTF-8 编码开销。
立即学习“Python免费学习笔记(深入)”;
- 必须用
pip install msgpack(不是msgpack-python,后者已废弃) - 关键开关:传参
strict_types=True,否则遇到datetime或自定义类会静默切到纯 Python 模式,性能暴跌 4 倍 -
dict的 key 只能是str;若用了int或bytes,会直接报ValueError: Unpack failed: unhashable type - 时间字段别用
default回调——提前转成毫秒整数:int(dt.timestamp() * 1000)
真正影响速度的从来不是“选什么格式”,而是你有没有关掉它的兼容性开关、有没有把非基础类型提前规整掉。很多团队测下来 msgpack 比 pickle 快 5 倍以上,不是因为 magic,只是因为它不做多余的事。


















