MySQL JSON 字段格式变更导致摘要不一致问题及解决方案
在实际开发中,为了增强数据结构的灵活性,MySQL 的 JSON
类型被广泛使用。然而,在使用过程中发现,MySQL 会对插入的 JSON 数据在存储或查询时进行自动格式化处理,例如:
- 调整键值对顺序(基于键名排序);
- 在 key 与 value 之间添加空格;
- 去除或添加某些结构性字符(如换行)。
这种格式调整虽然不影响 JSON 的语义,但在某些业务场景下却可能造成严重问题,例如基于 JSON 数据计算的摘要(如 SHA1 或 MD5)发生不一致。
现象举例
在业务逻辑中使用
{"key":"value"}
插入 MySQL。插入前计算摘要为:
1
SHA1("{"key":"value"}") = 228458095a9502070fc113d99504226a6ff90a9a
从 MySQL 查询结果为:
1
{"key": "value"}
(注意 key 和 value 之间多了一个空格)
查询结果的摘要为:
1
SHA1("{"key": "value"}") = e735dd8e68d8938109f02c383cc9d056c942a514
结果:两次摘要不同,无法用于数据一致性校验或签名验证。
根因分析
- MySQL JSON 类型底层以二进制 JSON 存储,查询时为了展示友好,可能会自动格式化。
- 不同语言或客户端(如 Java 的 Jackson、Go 的 encoding/json)对 JSON 的序列化顺序、空格控制也可能不同。
- 即使语义一致,JSON 文本结构上的任何变化都会导致摘要计算结果不同。
Golang 示例
创建 MySQL 测试表
|
|
测试代码
|
|
===== 测试用例: 紧凑JSON =====
原始JSON: {“name”:“Alice”,“age”:30,“active”:true}
Go计算的SHA1: 96179b68d41495dfd8f1c9bc7a093d92b56db6fe=== 数据库查询结果 ===
返回的JSON内容: {“age”: 30, “name”: “Alice”, “active”: true}
MySQL计算的摘要: 2882e0534aec7725267c9295e89f61a47a3326ad
❌ 摘要不一致: MySQL与Go计算结果不同
⚠️ JSON内容变化: 与原始输入存在差异
原始长度: 39 | 返回长度: 44===== 测试用例: 带空格JSON =====
原始JSON: { “name”: “Bob”, “age”: 25, “active”: false }
Go计算的SHA1: 704bf774b19ca144d1e554968a72a6bf136088e0=== 数据库查询结果 ===
返回的JSON内容: {“age”: 25, “name”: “Bob”, “active”: false}
MySQL计算的摘要: ce10e44c98b4332e900469a6b2ad740bbfe59343
❌ 摘要不一致: MySQL与Go计算结果不同
⚠️ JSON内容变化: 与原始输入存在差异
原始长度: 45 | 返回长度: 43
解决方案
✅ 方案一:业务侧生成摘要并写入
- 在写入数据库前,由业务逻辑使用紧凑 JSON(无空格、固定顺序)生成摘要值。
- 将该摘要作为独立字段插入数据库,避免依赖 MySQL 返回的 JSON 字符串。
|
|
✅ 方案二:存储 JSON 字符串,而非 JSON 类型
- 将
json
字段声明为TEXT
或VARCHAR
类型。 - 避免 MySQL 对 JSON 内容进行解析和格式化,存什么、取什么。
|
|
⚠️ 缺点是无法使用 JSON 类型的查询语法(如 JSON_EXTRACT)。
总结
MySQL 的 JSON 类型在插入与查询过程中可能会自动格式化,从而导致基于 JSON 字符串的摘要不一致。 为避免此类问题,应在业务层确保摘要计算的一致性,或避免使用 MySQL 的 JSON 类型进行签名和哈希计算的核心数据字段。