explain显示走索引但函数调用仍慢,根本原因是函数阻止索引下推且标量函数逐行执行;应避免where中对索引列用函数,改用范围查询。

为什么 EXPLAIN 看起来没问题,但函数调用还是慢?
MySQL 中自定义函数(UDF)或内置函数(如 DATE_FORMAT()、JSON_EXTRACT())在 WHERE 或 SELECT 中高频使用时,常成为性能瓶颈——即使 EXPLAIN 显示走了索引,实际执行仍卡顿。根本原因在于:函数会阻止索引下推(Index Condition Pushdown),甚至导致全表扫描;更隐蔽的是,标量函数在每行数据上重复执行,无法被优化器提前剪枝。
实操建议:
- 避免在
WHERE子句中对索引列使用函数,例如把WHERE DATE(create_time) = '2024-01-01'改成WHERE create_time >= '2024-01-01' AND create_time - 对 JSON 字段慎用
JSON_EXTRACT()做条件过滤,优先考虑生成虚拟列并建索引:ALTER TABLE t ADD COLUMN status INT AS (JSON_EXTRACT(data, '$.status')) STORED, ADD INDEX idx_status(status) - 确认函数是否为「确定性」(DETERMINISTIC):非确定性函数(如含
NOW()、RAND())无法被缓存,且可能干扰查询重写
哪些 MySQL 函数天生就慢?怎么绕开?
不是所有函数都适合线上高频调用。以下几类需特别警惕:
-
LIKE '%xxx':左侧通配符必然放弃索引,改用全文索引(FULLTEXT)或倒排表 + ES 同步 -
CONCAT()在大字段上拼接(尤其含TEXT类型)会触发隐式转换和临时表,可提前在应用层拼接或用生成列固化 -
GROUP_CONCAT()无ORDER BY时行为不可控,且受group_concat_max_len限制,超长会被截断;若用于分组聚合展示,优先考虑应用层组装 -
MD5()/SHA1()等哈希函数 CPU 开销大,批量计算时易拖慢 QPS,应尽量前置到写入阶段计算并存储
如何定位是函数本身慢,还是函数+数据联合慢?
单看慢日志(slow_query_log)只能知道整条 SQL 慢,无法拆解函数耗时。必须结合运行时诊断:
- 启用
performance_schema并查询events_statements_history_long,过滤出含函数名的 SQL,观察TIMER_WAIT和LOCK_TIME占比 - 用
sys.statement_analysis视图快速识别高延迟函数调用模式,例如:SELECT * FROM sys.statement_analysis WHERE query LIKE '%JSON_EXTRACT%' - 对疑似慢函数做隔离测试:单独执行
SELECT func(col) FROM t LIMIT 1000,对比SELECT col FROM t LIMIT 1000的执行时间,差值即为函数开销基准
UDF 和存储函数有哪些隐藏代价?
自定义函数(尤其是 C/C++ 编写的 UDF)看似灵活,但极易引入稳定性与性能问题:
- UDF 每次调用都需跨用户态/内核态,上下文切换成本高;若函数内部有网络/磁盘 I/O(比如调外部 HTTP 接口),会彻底阻塞整个连接线程
- 存储函数(
CREATE FUNCTION)默认以DETERMINISTIC声明,但若实际非确定却未声明NOT DETERMINISTIC,会导致主从不一致或复制中断 - 函数返回值类型不匹配(如定义为
INT却返回字符串)会触发隐式转换,且该转换发生在每一行,放大 CPU 使用率 - MySQL 8.0+ 对函数内联支持有限,无法像 PostgreSQL 那样自动内联展开,复杂逻辑仍需解释执行
真正棘手的从来不是“怎么写函数”,而是“要不要在这里用函数”。多数场景下,把计算逻辑下沉到应用层、或用物化视图/汇总表预计算,比硬扛一个慢函数更可靠。











