MySQL 5.7+ 的 JSON 类型是带格式校验、二进制存储和路径查询能力的原生类型,非简单字符串;插入非法 JSON 直接报错,支持 JSON_EXTRACT()、->> 取值及虚拟列索引优化查询。

MySQL 5.7+ 的 JSON 类型不是“存字符串”,而是带校验和索引能力的原生类型
直接用 VARCHAR 存 JSON 字符串,看似简单,但会丢失校验、无法用路径函数查询、不能建虚拟列索引。MySQL 原生 JSON 类型会在插入时验证格式合法性,非法 JSON 直接报错:Invalid JSON text。它还支持二进制存储结构,让 JSON_EXTRACT() 等操作更快。
建表时声明字段类型为 JSON 即可:
CREATE TABLE products ( id INT PRIMARY KEY, specs JSON );
- 插入必须是合法 JSON:支持对象
{}、数组[]、数字、字符串、null;不支持尾随逗号、单引号、未转义双引号 -
INSERT INTO products VALUES (1, '{"cpu":"i7","cores":8}');✅ -
INSERT INTO products VALUES (1, "{'cpu':'i7'}");❌(单引号 + 无校验) - 从字符串转换需显式调用
CAST('{"a":1}' AS JSON),否则自动转为字符串类型
用 JSON_EXTRACT() 和 -> 操作符取值,注意返回带引号的字符串
JSON_EXTRACT(specs, '$.cpu') 和简写 specs -> '$.cpu' 功能一致,都返回 JSON 类型值——这意味着字符串结果会带双引号,比如 "\"i7\""。如果要在 WHERE 或 ORDER BY 中直接比较,容易因引号导致匹配失败。
- 安全取字符串值:用
specs ->> '$.cpu'(注意是两个>),它自动去引号并转为 MySQL 字符串类型 - 取数组第 2 个元素:
specs -> '$[1]'(0-indexed),或specs ->> '$[1]'去引号 - 路径中含空格或特殊字符:用双引号括键名,如
$.["screen size"] - 路径不存在时返回
NULL,不会报错;可用COALESCE(specs ->> '$.ram', '8GB')设默认值
WHERE 条件里查 JSON 字段,别直接用 = 比较整个 JSON
整字段比对效率低且不可索引。应提取具体键值后判断,尤其要避免 JSON_EXTRACT(specs, '$.cpu') = '"i7"' 这种写法——右边手动加引号极易出错。
- 正确写法:
WHERE specs ->> '$.cpu' = 'i7' - 查数组是否包含某值:
WHERE JSON_CONTAINS(specs, '"SSD"', '$.storage')(注意第三个参数是路径,第二个参数必须是 JSON 格式字符串) - 查对象是否存在某个键:
WHERE JSON_CONTAINS_PATH(specs, 'one', '$.gpu')('one'表示至少一个匹配) - 性能关键:对高频查询的 JSON 键,建议用虚拟列 + 索引:
ALTER TABLE products ADD cpu_brand VARCHAR(20) AS (specs ->> '$.cpu') STORED, ADD INDEX idx_cpu (cpu_brand);
JSON_SET()、JSON_REPLACE()、JSON_INSERT() 的行为差异很实际
更新 JSON 字段时,三个函数名字像,但逻辑不同,选错会导致数据意外覆盖或静默失败。
-
JSON_SET(json, path, value):路径存在则替换,不存在则插入(类似 upsert) -
JSON_REPLACE(json, path, value):路径不存在就什么也不做,不报错也不插入 -
JSON_INSERT(json, path, value):路径存在就跳过,只在不存在时插入 - 批量更新多个键:每个
path,value对都要单独写,不支持一次传数组;例如JSON_SET(specs, '$.cpu', 'i9', '$.year', 2024) - 删除键要用
JSON_REMOVE(specs, '$.temp'),没有JSON_DELETE
虚拟列和函数索引虽能加速查询,但 JSON 字段本身无法被直接索引;真正高频、固定结构的字段,长期看还是拆成关系型字段更稳。JSON 适合配置项、扩展属性、半结构化日志这类变化频繁、schema 不确定的场景。


















