社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  DATABASE

MySQL Json 怎么处理,8.021后有什么变化,哪些不应该用JSON

AustinDatabases • 1 周前 • 70 次点击  
❝

开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, OceanBase, Sql Server等有问题,有需求都可以加群加群请联系 liuaustin3 ,(共3400人左右 1 + 2 + 3 + 4 +5 + 6 + 7 + 8 )(1 2 3 4 5 6 7 8群已经爆满  9群 为纯聊天群,默认不加入不得发广告,自己公众号文章链接等,发一次直接踢,默认加入8群,开10群PolarDB专业学习群115+)

本文重点(读完就能上手)

① JSON 类型给关系型表加了"弹性扩展区"——固定字段用 VARCHAR/INT,变化多端的塞 JSON;

② Generated Column(虚拟生成列)+ 索引 是 JSON 性能的核心,8.0 优化器自动识别,你不用改查询;

一、为什么要用 JSON 类型(而不是 TEXT)

MySQL 从 5.7 开始支持原生 JSON 类型,8.0 又加入了 JSON_TABLE、Multi-Valued Index 等杀手功能。它不是 MongoDB 的替代品,而是给关系型表开了一扇弹性扩展的门。

用 TEXT 存 JSON 字符串有三个问题:写入不校验、每次读取要重新解析、无法建索引。原生 JSON 类型在写入时自动校验合法性、内部用二进制格式存储(键排序、快速定位)、支持全套 JSON 函数和 Generated Column 索引。

一句话:99% 场景用 JSON 类型,只有纯日志写入永远不按内部字段查询的场景才考虑 TEXT。

二、路径语法:"->" 和 "->>" 的生死区别 MySQL JSON 路径从 $ 开始,点 . 访问对象键,[n] 访问数组下标。

但有一个让无数人踩坑的细节:col->path 和 col->>path 返回值类型不同。

原因很简单:MySQL 做字符串比较时,"Apple"(带引号)和 Apple(纯文本)是两个不同值。你写 WHERE attributes->'$.brand' = 'Apple',MySQL 实际在比较 "Apple" = "Apple"——JSON 值带引号,而右侧是纯文本,大概率匹配失败。

三、Generated Column:JSON 性能的核心 JSON 列不能直接建索引——MySQL 不允许 CREATE INDEX ON t(json_col),因为 JSON 没有固定的比较规则。但可以通过 Generated Column(生成列) 间接地给 JSON 里的字段建索引。

步骤分两步:先给 JSON 里的高频字段加一个 VIRTUAL 生成列(查询时实时计算,平时不占存储空间),再给这个生成列建普通索引。

-- Step 1: 给 brand(品牌)和 weight(重量)加生成列
ALTER TABLE products
    ADD COLUMN brand VARCHAR(50)
        GENERATED ALWAYS AS (attributes->>'$.brand') VIRTUAL,
    ADD COLUMN weight_g INT
        GENERATED ALWAYS AS (CAST(attributes->>'$.weight' AS UNSIGNED)) VIRTUAL;

-- Step 2: 给生成列建索引(MySQL 8.0 优化器自动识别)
CREATE INDEX idx_brand ON products (brand);
CREATE INDEX idx_weight ON products (weight_g);

-- Step 3: 直接用生成列名查询,或者继续用 JSON 写法——都能走索引!
EXPLAIN SELECT name FROM products
WHERE brand = 'Nike';         -- ✅ 走 idx_brand
EXPLAIN SELECT name FROM products
WHERE attributes->>'$.brand' = 'Nike';  -- ✅ 也自动走 idx_brand(8.0 优化器)

VIRTUAL 生成列不占实际存储空间(只存计算表达式),有二级索引时查询走索引、不计算,无开销。这是零成本高性能方案。

四、JSON_TABLE:把数组展开成行(8.0 杀手级功能) 如果说 Generated Column 解决了"给 JSON 单值建索引",那 JSON_TABLE 解决了"把 JSON 数组展开成多行"。它相当于 PostgreSQL 的 jsonb_each——把一个 JSON 数组字段变成一张临时表,然后跟普通表一样做 JOIN、GROUP BY、聚合。

电商订单表的 items 字段是 JSON 数组(每个订单可能有多个商品),要统计 SKU 销量,用 JSON_TABLE 一行搞定:

-- 订单表 items 格式: [{"sku":"SHOE-001","qty":2},{"sku":"SHIRT-003","qty":3}]
-- 用 JSON_TABLE 展开每个订单项,再聚合
SELECT jt.sku, SUM(jt.qty) AS total_sold
FROM orders o
CROSS JOIN JSON_TABLE(
    o.items,                    -- 要展开的 JSON 列
    '$[*]'                      -- 遍历数组每个元素
    COLUMNS (
        sku VARCHAR(64) PATH '$.sku',
        qty INT PATH '$.qty'
    )
) AS jt
GROUP BY jt.sku
ORDER BY total_sold DESC;

这段 SQL 的效果:CROSS JOIN 把每个订单的 items 数组拆成多行(每行是一个订单项),然后聚合统计。MySQL 8.0.14+ 对 JSON_TABLE 做了引擎级优化,不是应用层循环,配合 Generated Column 索引性能很好。

五、JSON 修改函数:部分更新不重写整个 JSON 高频场景是:用户下单后要更新商品 attributes 的 price 字段。传统做法是应用层取出来、改完、整个 JSON UPDATE 回去。但 MySQL 8.0 提供 JSON_SET / JSON_REPLACE / JSON_REMOVE 等函数,做部分更新——只改叶子节点,不重写整个 JSON。实测快 3~5 倍。

-- JSON_SET: 存在则更新,不存在则新增
UPDATE products
SET attributes = JSON_SET(attributes, '$.price', 699.00, '$.discount', true)
WHERE sku = 'SHOE-001';

-- JSON_REMOVE: 删除字段
UPDATE products
SET attributes = JSON_REMOVE(attributes, '$.discount')
WHERE sku = 'SHOE-001';

-- JSON_ARRAY_APPEND: 向数组追加元素
UPDATE products
SET attributes = JSON_ARRAY_APPEND(attributes, '$.color', 'navy')
WHERE sku = 'SHOE-001';

六、JSON 性能调优和调试技巧 第一个技巧:用 JSON_PRETTY() 格式化输出。存进去的 JSON 在存储前会被压缩(去掉所有空格换行),SELECT 出来是一长串很难看。调试时用 SELECT JSON_PRETTY(attributes) FROM products 就能得到缩进后的格式,方便肉眼检查。JSON_PRETTY 不影响存储,只改变返回格式。

第二个技巧:JSON_STORAGE_SIZE() 看实际占用。SELECT JSON_STORAGE_SIZE(attributes) FROM products 返回 JSON 二进制存储的字节数,跟 TEXT 存原始字符串对比一下——你会发现原生 JSON 因为键排序和去冗余,实际占用比 TEXT 小不少。这也是 JSON 类型的隐性优势之一。

第三个技巧:用 EXPLAIN 确认 Generated Column 索引生效。写完查询后加 EXPLAIN 看执行计划,type 应该是 ref 或 range,key 列应该显示你的索引名。如果还是 ALL(全表扫描),检查是不是路径写错了、或者 MySQL 版本低于 8.0 不能自动识别。

第四个技巧:JSON 键名大小写敏感。attributes->>' .brand' 是两个不同的路径。设计 JSON schema 时统一用 snake_case 或 camelCase,别混用。我推荐 snake_case——跟数据库列名风格一致,团队协作时不会混乱。

第五个技巧:JSON 列上限 1GB,但实际别超过几 MB。MySQL JSON 列理论上能存 1GB,但超过几 MB 后 JSON 函数的性能会显著下降,而且 InnoDB 溢出到 BLOB 页后还会影响表级性能。一个经验规则:单个 JSON 控制在 100KB 以内,超过就该拆表了。

三个必须知道的技术点 知识 1:-> 返回 JSON 值(带引号),->> 返回 SQL 标量(无引号)。WHERE 和 ORDER BY 里只能用 ->>。

知识 2:VIRTUAL 生成列不占存储空间,有二级索引时查询走索引不计算。STORED 才占空间(INSERT/UPDATE 时计算存储)。默认用 VIRTUAL。

知识 3:MySQL 8.0 优化器足够聪明——你写 attributes->>'$.brand',它自动匹配到 brand 生成列上的索引。但为了可读性建议直接写生成列名。

JSON_VALUE() 表达式索引(8.0.21+ 新写法)

8.0.21 引入了更简洁的写法——直接在 JSON_VALUE() 表达式上建索引,不需要先加 Generated Column:

-- 直接建表达式索引,不用先 ALTER TABLE
CREATE INDEX idx_attr_brand ON products
    ((JSON_VALUE(attributes, '$.brand')));

-- MySQL 自动识别这个表达式,查询走索引
SELECT * FROM products
WHERE JSON_VALUE(attributes, '$.brand') = 'Nike';

-- 原来的 ->> 写法也自动匹配这个索引
SELECT * FROM products
WHERE attributes->>'$.brand' = 'Nike';

JSON_VALUE() 的优势是可以指定返回类型——JSON_VALUE(attributes, '$.price' RETURNING DECIMAL(10,2)),比 CAST 更清晰。如果你的 MySQL 版本 ≥ 8.0.21,推荐用这种写法,比 Generated Column 更干净。

实际业务反例:JSON 什么时候不该用

说了这么多 JSON 好的地方,但它不是银弹。有几种场景绝对不该用 JSON:

第一,高频查询 + 频繁过滤的字段。如果一个字段在 50% 的查询里都出现在 WHERE 里,直接建固定列。JSON + Generated Column 虽然能补索引,但每次 INSERT/UPDATE 都要额外计算生成列值,写入开销比纯固定列大。

第二,需要 JOIN 的字段。如果你要"按 attributes 里的 category 字段 JOIN 到类别表",别用 JSON。Generated Column + JOIN 能跑,但查询计划会很丑,而且 MySQL 优化器对这种场景的支持不如原生列。老实建 category_id 外键。

第三,需要唯一约束的字段。JSON 列不能直接建 UNIQUE 约束(MySQL 不支持),Generated Column 可以,但语义上很奇怪。如果一个字段需要唯一,它本质上就是重要的业务字段,应该是固定列。

总结一下 JSON 的正确姿势:JSON 存"不查或很少查"的弹性字段,固定列存"高频查"的业务字段。70/30 原则——70% 字段固定列,30% 变化字段塞 JSON。别反过来。

微软通过AI 变身企业数据分析软件公司,是做数据库和操作系统不赚钱了???

普通人的AI 终极武器,怎么3分钟把一周的活干完--workbuddy

PostgreSQL 版本升级方法总结,具体pg_upgrade怎么操作

与OceanBase集中式摸爬滚打的4个月,我得到了什么 ?
醋评 数据库行业 “不行了”  ---来自五彩斑斓乌鸦的 3336个字
《没有人为不需要的性能付费 经济下行,正在倒逼数据库"做减法"》

PostgerSQL 14-17备份的变化 PG17更贴近商业数据库 与 实际命令

PostgreSQL 怎么用好高版本的PG调优--PG14-PG18

同学问 PG17 的备份比老的版本 好哪了? 你给总结总结 !!

算法领主与数据农奴:AI时代的不能说的问题-- 此文为AI临时工所做与公众号作者无关

《AI为什么迟迟进不了企业核心系统?我总结了八个原因》

《AI不是出事了,而是我们开始看到它的代价》
NOSQL 怎么翻盘,降本增效为企业节省资源,--DTCC 通过NOSQL给企业系统瘦身

怎么AI设定评估成本模型思考

MySQL 写不进去数据,程序报错,谁的问题?

从亚马逊 AGI 部门裁员看 AI 商业逻辑的必然转向  -- 资本不会给AGI 半点脸

比起简单的Skill技能,我更想建立Agent Skill的系统思维能力--- 感谢本书作者答疑解惑

  MongoDB 全文索引 与 展示查询数据的一部分,提高性能

体现价值-我们靠PostgreSQL迁移PolarDB,给公司省下了100万 “巨款”

《告别迁移焦虑:OceanBase MySQL 模式能否兼容 DBA 的“祖传”运维 SQL?》

干数据库不是买白菜:光盯着License几毛钱,看不见300台机器的电费?

一个秘密,不是你 SQL 写对了,是优化器帮“擦了屁股”  客户问迁移后为什么快了--迁移到PolarDB后的故事

AI 时代,我却用不上一个靠谱的数据库产品

AI 引入后,MySQL 列权限控制,插入,更新,读取,删除 --有了AI 真是越帮越忙

PostgreSQL 大表改字段卡死的问题解决了吗?  解决了方案在此

AI 引入DBA 工作,造成工作量增加,忙不过来,根本忙不过来!!!

三无项目导致MongoDB 持续1406% CPU 问题解决





Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/202577