商品属性存在 JSON 列里,查询 WHERE attrs->>'$.brand' = 'acme' 扫了 60 万行,explain 的 key 是 NULL,耗时 5 秒。日志里能对上时间点,这种反而好排。

对 JSON 列做 ->> 提取得到的是字符串表达式,普通索引建在整列上,没法定位到某个路径。MySQL 5.7 之后支持生成列加索引,直接对表达式建索引要到 8.0.13。这一点先确认清楚,后面就不用反复试。

ALTER TABLE items ADD COLUMN brand VARCHAR(32)
  GENERATED ALWAYS AS (attrs->>'$.brand') VIRTUAL,
  ADD INDEX idx_brand (brand);

「JSON 字段索引」改完先别急着全量,挑一台机器放半天,真有问题这段时间就能看出来。偶发问题先统计频率,一小时几次和一天一次,查法完全不同。

改完顺手把文档补一句,省得下次自己重新推一遍。监控和日志的时间要对上,时区不一致会多绕很大一圈。

处理上按这个顺序:把要查的路径抽成虚拟生成列 类型写清楚;在生成列上建普通索引 查询条件改成比较生成列;查询里的表达式要和生成列定义完全一致 差一个引号都用不上;多个路径都要查时再考虑多值索引。

结果对不对很好判断,explain 的 key 变成 idx_brand rows 降到几百 查询从 5 秒降到几十毫秒。

作者 9823189