函数索引仅对表达式完全匹配的查询生效,如upper(name)='john';必须用双括号语法且函数名大写;仅支持确定性函数;mysql 8.0.13+才稳定支持;需用explain验证实际使用效果。

函数索引能直接让 WHERE UPPER(name) = 'JOHN' 这类带函数的条件走索引,但前提是表达式完全匹配、函数确定、语法写对——否则它压根不会生效,还可能让你误以为“加了索引就万事大吉”。
函数索引只对字面量完全匹配的查询生效
你建了 CREATE INDEX idx_upper_name ON users ((UPPER(name))),那只有下面这种查询能命中:
-
WHERE UPPER(name) = 'JOHN'✅ -
ORDER BY UPPER(name)✅(仅限 ASC/DESC 明确时) -
WHERE name = 'john'❌(普通列条件,不触发函数索引) -
WHERE LOWER(name) = 'john'❌(函数不同,索引不复用) -
WHERE UPPER(name) LIKE 'JO%'❌(LIKE带通配符,优化器无法下推)
本质是 MySQL 把 UPPER(name) 的结果当成一个虚拟列来索引,查询条件必须和建索引时的表达式“一模一样”,连空格、大小写都不能差。
必须用双括号语法,且函数名要大写
这是建索引第一步就卡住的高频错误。MySQL 8.0 要求函数表达式必须被外层双括号包裹,且函数名在严格模式下必须大写:
- ✅ 正确:
CREATE INDEX idx_date ON orders ((DATE(created_at))) - ✅ 正确:
CREATE INDEX idx_domain ON users ((SUBSTRING_INDEX(email, '@', -1))) - ❌ 错误:
CREATE INDEX idx_date ON orders (DATE(created_at))(少一层括号,报错ERROR 1064) - ❌ 错误:
CREATE INDEX idx_date ON orders ((created_at))(单列加括号 ≠ 函数索引,报错Functional index expression cannot be a column reference) - ❌ 错误:
CREATE INDEX idx_date ON orders ((date(created_at)))(小写函数名,在sql_mode=STRICT_TRANS_TABLES下失败)
注意:DATE_FORMAT() 这类非确定性函数直接禁止使用,建索引会报错,别试。
支持哪些函数?关键看是否“确定性”
函数索引底层复用生成列机制,所以只能用确定性、无副作用、可比较的函数:
- ✅ 安全可用:
UPPER()、LOWER()、DATE()、YEAR()、SUBSTRING()、JSON_EXTRACT()(需配合CAST(... AS CHAR)显式转类型) - ✅ 算术表达式也行:
((price * (1 + tax_rate))),但别太复杂,否则插入/更新时计算开销明显上升 - ❌ 绝对禁用:
NOW()、RAND()、UUID()、CURDATE()、子查询、用户变量、自定义函数
验证是否支持最简单的方法:试着建个 GENERATED ALWAYS AS 虚拟列,能成功,函数索引大概率也能用。
建索引前必须确认 MySQL 版本与执行环境
函数索引从 MySQL 8.0.13 才开始稳定支持;低于这个版本即使语法通过,索引也不会生效。另外两个容易忽略的点:
- 线上大表建函数索引会触发全表扫描计算表达式值,耗时可能远超预期,务必先在从库或低峰期测试
- 如果用的是容器化部署,确认
log_bin_trust_function_creators已设为1(尤其涉及自定义函数或某些 JSON 函数时) - 验证是否真起作用,不能只看
SHOW INDEX FROM t1,得查EXPLAIN,并确认key列显示索引名、Extra不含Using filesort或Using temporary
最常被跳过的一步:建完索引后没跑 EXPLAIN 核对执行计划,就直接上线——结果发现查询还是全表扫描,问题出在条件写法和索引表达式不一致,而不是索引本身没建好。











