mysql 8.0函数索引仅加速严格匹配确定性表达式的查询,要求表达式无子查询、变量等,且where条件必须与索引定义完全一致,dml时实时计算并占用存储空间,需用explain验证命中。

MySQL 8.0 的函数索引(FUNCTIONAL INDEX)能直接加速带表达式的 WHERE、ORDER BY 或 JOIN 查询,但必须满足几个硬性条件,否则它根本不会被优化器选用。
函数索引只对确定性表达式生效
MySQL 要求索引表达式必须是 DETERMINISTIC(确定性)的,且不能包含子查询、用户变量、存储函数或非固定时区的日期函数。常见踩坑点:
-
SUBSTRING(email, 1, 5)✅ 可建索引 -
UPPER(name)✅ 可建索引(UPPER是内置确定性函数) -
NOW()❌ 不允许 —— 每次执行结果不同 -
CONVERT(name USING utf8mb4)❌ 隐式转换可能失效,且非标准确定性行为 -
(price * 1.1)✅ 允许,但注意类型隐式提升可能导致索引失效(如price是DECIMAL,乘法后精度变化)
WHERE 条件必须严格匹配索引表达式
优化器不会自动“推导等价关系”,哪怕语义相同。例如:
你建了 CREATE INDEX idx_upper_name ON users ((UPPER(name)));
那么只有下面这种查询能走索引:
SELECT * FROM users WHERE UPPER(name) = 'JOHN';
而这些写法都不走索引:
-
WHERE name = 'john'(大小写敏感,没用到 UPPER) -
WHERE UPPER(name) LIKE 'JO%'(LIKE前缀匹配在函数索引上仍有效,但%JO%就不行) -
WHERE name LIKE 'joh%'(没包装UPPER,无法匹配索引定义)
INSERT/UPDATE 会触发函数计算并写入索引页
函数索引不是“懒计算”,每次 DML 都会实时计算表达式值并维护索引项,带来额外开销:
- 写放大:每行插入/更新需多算一次表达式 + 多写一个索引条目
- 锁竞争:函数计算本身不加锁,但索引页修改仍参与 B+ 树 latch 竞争
- 空间占用:函数索引列不存原始值,只存计算结果,但依然占
innodb_page_size(默认 16KB)空间 - 字段长度限制:表达式结果长度不能超过 3072 字节(InnoDB 单键最大长度),否则建索引失败
比如 MD5(content) 安全但冗余;LEFT(content, 200) 更轻量且实用。
用 EXPLAIN 验证是否命中函数索引
关键看 EXPLAIN 输出中的 key 和 Extra 字段:
-
key: idx_upper_name→ 明确用了该函数索引 -
Extra: Using index→ 覆盖索引(无需回表) -
Extra: Using where; Using index→ 表达式已用于过滤,且索引覆盖 - 如果
key为空,或出现Using filesort/Using temporary,说明函数索引未生效,得检查表达式是否被完整写出、是否含不可索引操作
特别注意:SHOW INDEX FROM users 中函数索引的 Column_name 列显示为 (UPPER(`name`)),括号和反引号是标识符的一部分,不能省略。











