函数索引仅在where条件中字面完全匹配索引定义的表达式时才生效,否则全表扫描;mysql 8.0强制要求create index中函数表达式必须用双括号包裹,且仅支持确定性函数。

函数索引只有在 WHERE 条件里字面写出和索引定义一模一样的表达式时,才会被用上;否则就是全表扫描。
CREATE INDEX 必须用双括号,否则语法报错
MySQL 8.0 强制要求函数索引的表达式必须用外层括号包裹,这是硬性语法,不是可选风格。
-
CREATE INDEX idx_lower_email ON users ((LOWER(email)));✅ 正确:双括号 + 确定性函数 -
CREATE INDEX idx_bad ON users (LOWER(email));❌ 报错:缺外层括号,ERROR 1064 -
CREATE INDEX idx_bad2 ON users ((email));❌ 报错:括号里不是表达式,是纯列名 -
CREATE INDEX idx_bad3 ON users (SUBSTRING(name, 1, 10));❌ 报错:缺外层括号,且 SUBSTRING 不带长度声明时可能被拒
WHERE 条件必须和索引定义“逐字符一致”
优化器不做语义等价推导。哪怕逻辑完全等价,只要写法不同,就无法命中函数索引。
- 建了
CREATE INDEX idx_date_created ON orders ((DATE(created_at))); - 只有
WHERE DATE(created_at) = '2026-05-06'能走索引 WHERE created_at >= '2026-05-06' AND created_at ❌ 不走 —— 没包装 DATE()-
WHERE DATE(created_at) >= '2026-05-06'❌ 不走 —— 函数索引只支持等值或前缀 LIKE(如LIKE '2026%'),不支持范围比较 -
WHERE LOWER(email) LIKE 'admin%'✅ 可走 —— 前缀匹配在函数索引上仍有效
函数索引只接受确定性表达式,NOW()、CONVERT() 都不行
MySQL 要求表达式每次执行必须返回相同结果,且不能依赖会话变量、子查询、非固定时区函数等。
- ✅ 允许:
SUBSTRING(email, 1, 5)、UPPER(name)、price * 1.1(注意精度隐式提升风险) - ❌ 禁止:
NOW()(每次不同)、CONVERT(name USING utf8mb4)(非标准确定性行为)、(SELECT COUNT(*) FROM logs)(含子查询) - ⚠️ 高危:
MD5(content)虽然确定但占 32 字节,若 content 很长,表达式结果可能超 3072 字节限制,建索引失败
用 EXPLAIN 验证是否真生效,别信感觉
函数索引是否命中,唯一可信依据是 EXPLAIN 输出中的 key 和 Extra 字段。
-
key: idx_lower_email→ 明确用了该函数索引 -
Extra: Using index→ 覆盖索引,无需回表 -
Extra: Using where; Using index→ 表达式已用于过滤,且索引覆盖 -
key: NULL或出现Using filesort/Using temporary→ 函数索引未生效,得回头检查表达式是否完整写出、有无不可索引操作
真正容易被忽略的是:DML 时函数索引会实时计算并写入,带来写放大和锁竞争——它不是“只读加速”,而是“读写双开销”。上线前务必在写压力场景下压测。











