MySQL 5.7虚拟列索引失效主因是查询未引用虚拟列、类型不一致或混用原始字段;VIRTUAL适用于读多写少过滤场景,STORED仅在需排序分组或从库建索引时选用。

MySQL 5.7 不支持函数索引,WHERE DATE(created_at) = '2024-01-01' 或 WHERE extra->>'$.status' = 'active' 这类写法必然触发全表扫描;唯一可靠解法是用虚拟列把计算逻辑“固化”到列定义里,再建索引。
为什么加了虚拟列和索引,EXPLAIN 还显示 type: ALL?
索引没走,不是建得不对,就是用得不对。重点看 EXPLAIN FORMAT=JSON 输出里的 "key" 和 "type" 字段。
- 查询中混用原始字段:比如同时写了
WHERE v_status = 1 AND extra->>'$.status' = 'active',优化器可能放弃虚拟列索引 - 类型不一致:虚拟列是
TINYINT,但写成WHERE v_status = '1'(字符串),触发隐式转换,索引失效 - 根本没引用虚拟列:仍沿用旧写法
WHERE JSON_EXTRACT(extra, '$.status') = 'active',当然不走新索引 -
LIKE '%abc'这种后缀模糊查也能用虚拟列加速——加个REVERSE()虚拟列即可,不用冗余存反转字符串
VIRTUAL 和 STORED 到底该选哪个?
绝大多数场景选 VIRTUAL 就够了;STORED 只在明确需要 ORDER BY/GROUP BY 或从库单独建索引时才值得考虑。
-
VIRTUAL:不占磁盘空间,查询时实时计算,索引值物化存储——适合读多写少、纯过滤场景 -
STORED:写入时计算并落盘,二级索引可完整引用,支持排序分组——但每次INSERT/UPDATE都多一次计算+写入,写负载明显上升 - 不能从
VIRTUAL改成STORED,必须DROP COLUMN后重建 - 从库想靠索引减压?必须主库建
STORED虚拟列,再在从库对它建索引;VIRTUAL列即使加了索引,从库仍需实时计算
哪些表达式能用?哪些会直接报错?
MySQL 启动时会校验虚拟列表达式是否 immutable(不可变),非确定性函数直接拒绝建表。
- ✅ 允许:
JSON_EXTRACT(extra, _utf8mb4'$.age')、JSON_UNQUOTE(extra->>'$.status')、DATE_FORMAT(create_time, '%Y-%m')、MONTH(created_at) - ❌ 禁止:
NOW()、CURRENT_USER()、CONNECTION_ID()、RAND()、跨表引用、自增列、其他生成列 - JSON 字段推荐用
->>'$.key'提取字符串,再显式转类型(如VARCHAR(32)),避免隐式转换风险 - 表达式只能引用本表已有字段,不能含子查询或变量
最易被忽略的一点:虚拟列本身是只读的,INSERT 或 UPDATE 时不能给它赋值;但更关键的是——一旦选错 STORED,写入抖动和聚簇索引膨胀会悄悄拖垮整个实例的缓冲池命中率,而这个问题在低流量测试环境几乎无法暴露。


















