能,但需显式创建函数索引且where条件必须与索引定义严格一致;upper()等标量函数支持,now()等不支持;mysql 5.7及更早不支持;explain中key显示为func,需结合rows和extra判断是否真生效。

MySQL 8.0+ 函数索引到底能不能走索引
能,但只在明确创建了函数索引的前提下——WHERE UPPER(name) = 'JOHN' 这种写法,即使 name 有普通索引也完全用不上,优化器直接放弃索引扫描。
函数索引不是“让普通索引支持函数查询”,而是你得显式声明:这个函数表达式本身需要被索引。比如你常查 UPPER(email),就得单独建一个 INDEX idx_upper_email ((UPPER(email)))。
-
UPPER()、TRIM()、SUBSTRING()等标量函数可以用于函数索引;NOW()、RAND()、用户自定义函数(UDF)不行 - MySQL 5.7 及更早版本不支持函数索引,
CREATE INDEX会报错ERROR 1064 - 函数索引列在
EXPLAIN的key字段里显示为函数表达式,比如func,而不是原始列名
为什么 WHERE 中写法必须和函数索引定义严格一致
优化器做索引匹配时是字符串级比对,不是语义等价推导。你建了 INDEX idx_email_trim ((TRIM(LEADING ' ' FROM email))),那查询就必须写成 WHERE TRIM(LEADING ' ' FROM email) = 'a@b.com',哪怕 TRIM(email) 逻辑等价也不行。
- 大小写敏感:
UPPER(email)和upper(email)在定义和使用中必须一致(MySQL 默认不区分,但依赖 SQL mode) - 空格和括号不能省:
(email)≠email,函数索引定义里多了一对括号,查询里也得带上 - 隐式类型转换会断链:如果
email是VARCHAR,但你在函数里写了UPPER(CAST(email AS CHAR)),那就和没 cast 的索引不匹配
EXPLAIN 看不出函数索引是否生效?检查这几个地方
光看 type: ref 或 key: idx_xxx 不够,函数索引容易“假命中”——看起来走了索引,实际是全表扫描后过滤。
- 重点看
rows:如果值接近表总行数,基本没真正利用函数索引的有序性 - 看
Extra字段:出现Using where是正常;但若同时有Using index condition或Using index,说明索引下推(ICP)或覆盖索引生效了 - 用
SHOW INDEX FROM tbl确认函数索引是否真的存在,且Column_name列显示为func(不是真实列名) - 执行
SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 'tbl',检查INDEX_COMMENT是否为空——非空可能表示索引损坏或未加载
函数索引的维护成本和常见翻车点
函数索引不是零成本的“魔法开关”,它会拖慢写操作,而且容易在迁移或升级时失效。
- INSERT/UPDATE 每次都要计算函数值并写入索引页,比普通索引多一次表达式求值开销
- 备份恢复后,函数索引可能丢失(尤其用
mysqldump --skip-triggers时),因为CREATE INDEX语句里含函数表达式,部分工具解析失败 - 从 MySQL 5.7 升级到 8.0 后,旧表不会自动补函数索引,必须手动重建;而
ALTER TABLE ... ALGORITHM=INPLACE对函数索引不支持,只能COPY - JSON 函数如
JSON_EXTRACT()虽然能建函数索引,但一旦 JSON 结构变动(比如字段重命名),索引就彻底失效,且无任何警告
最麻烦的是:函数索引无法被 ANALYZE TABLE 正确采样统计,优化器对它的基数估计经常严重失真,导致本该走索引的查询被弃用——这时候只能加 FORCE INDEX 临时绕过,但治标不治本。










