函数索引是什么
函数索引(8.0.13+ 支持)按表达式而不是按列建索引:MONTH(created_at)、CAST(data->>'$.type' AS CHAR(16)) 这类表达式可以成为索引键。实现上 MySQL 会隐式创建一个隐藏虚拟列再在其上建索引。它解决的是「列被函数包住导致索引失效」的老问题,但代价是可读性下降,并且查询里的表达式必须与索引表达式完全一致才能命中。
基本信息
| 版本要求 | MySQL 8.0.13 起支持表达式索引,同版本起支持 JSON 路径表达式索引 |
|---|---|
| 语法要点 | 表达式必须用额外一对括号包起来:(表达式) |
| 实现方式 | 隐藏虚拟列 + 普通索引 |
| 匹配条件 | 查询中的表达式需与索引表达式完全一致才可使用 |
| 查看 | SHOW INDEX 与 information_schema 中会出现隐藏列名 |
典型使用场景
- 按时间片段查询:经常按 MONTH(created_at) 或 DATE(created_at) 过滤时避免全表扫描
- JSON 字段检索:对 data->>'$.type' 这类提取表达式建索引,支撑按 JSON 属性筛选
- 大小写不敏感检索:对 LOWER(email) 建索引,配合 LOWER() 查询稳定命中
- 表达式计算过滤:按 UPPER(code) 或截取后拼接的键值查询
- 老查询无法改写时:查询由框架生成不便改写,用函数索引兜住性能
创建与验证
创建与验证
-- 对月份表达式建索引,注意外层要多一对括号
ALTER TABLE appdb.orders ADD INDEX idx_month ((MONTH(created_at)));
-- 查询表达式必须与索引表达式完全一致才可能命中
EXPLAIN SELECT id FROM appdb.orders WHERE MONTH(created_at) = 10;
-- JSON 路径表达式索引(8.0.13+)
ALTER TABLE appdb.events ADD INDEX idx_js ((CAST(data->>'$.type' AS CHAR(16))));
EXPLAIN SELECT id FROM appdb.events WHERE CAST(data->>'$.type' AS CHAR(16)) = 'click';
-- 确认索引已注册,含隐藏列名
SHOW INDEX FROM appdb.orders WHERE Key_name = 'idx_month';与普通索引对比
| 维度 | 普通索引 | 函数索引 |
|---|---|---|
| 索引键 | 列值本身 | 表达式计算结果 |
| 命中条件 | 列直接比较即可 | 表达式必须完全一致 |
| 可读性 | 定义直白 | 需对照定义才能理解 |
| 维护成本 | 写入维护列值 | 写入时需计算表达式 |
| 优先顺序 | 首选 | 改写查询不可行时再用 |
能用改写解决时优先改写:把 MONTH(created_at) = 10 改成 created_at >= '2026-10-01' AND created_at < '2026-11-01',既走普通索引又语义清晰。函数索引适合确实无法改写的生成式查询。
表达式里的类型转换差异(例如 CHAR(16) 与 CHAR(20))会让优化器认为是不同表达式,索引直接失效,定义与查询要保持逐字一致。