




ICP 在函数列上完全不生效,因为存储引擎无法用函数结果匹配索引 B+ 树节点,只能由 server 层回表后计算过滤;所有对索引列的函数调用(如 UPPER、YEAR、CONCAT 等)均导致 ICP 失效,包括 CAST、COALESCE 及隐式类型转换。
MySQL 的 Index Condition Pushdown(ICP)优化只作用于索引字段的原始值比较,一旦 WHERE 条件中对索引列施加了函数调用(如 UPPER(col)、DATE(created_at)、col + 1),该列就不再满足“可下推”的前提——因为存储引擎层无法直接用函数结果去匹配索引 B+ 树节点。
此时优化器会退回到传统流程:先按索引范围取出所有主键(或聚簇索引记录),再由 server 层逐行计算函数并过滤。ICP 彻底失效,EXPLAIN 中的 Extra 字段也不会出现 Using index condition。
只要索引列出现在函数参数位置,无论函数是否确定性,都会阻断 ICP。常见失效写法包括:
WHERE UPPER(name) = 'JOHN'(即使 name 是索引列)WHERE YEAR(pay_time) = 2025(pay_time 有索引)WHERE status * 10 > 50WHERE CONCAT('A', code) LIKE 'AB%'WHERE JSON_EXTRACT(data, '$.type') = 'vip
'(即使 data 有生成列索引,函数本身仍不可下推)注意:CAST(col AS SIGNED)、COALESCE(col, 0) 同样失效——关键不是函数是否“简单”,而是它是否改变了索引列的原始存储形态。
如果必须按函数逻辑查询且想保留 ICP,唯一可靠方式是把函数计算提前固化到索引中:
ALTER TABLE t ADD COLUMN name_upper VARCHAR(64) STORED AS (UPPER(name)),再对 name_upper 建索引pay_year INT),查询时直接 WHERE pay_year = 2025
WHERE name LIKE 'john%' 可走索引且 ICP 有效,但 LOWER(name) LIKE 'john%' 不行不要依赖函数索引(MySQL 8.0.13+ 支持)来恢复 ICP——函数索引本身可被用于查找,但它不改变 ICP 的触发规则:server 层仍需在索引扫描后做二次计算,ICP 不会介入函数索引的谓词判断。
别只看 key 和 rows,重点检查 Extra 列:
EXPLAIN FORMAT=TRADITIONAL SELECT ... WHERE col = 'x' → 出现 Using index condition 表示 ICP 生效WHERE UPPER(col) = 'X' → Extra 变成空或仅 Using where,说明 ICP 已退出optimizer_trace 查看 icp_candidate 和 icp_used 字段,比 EXPLAIN 更明确最容易被忽略的是隐式类型转换:比如 WHERE int_col = '123' 虽无显式函数,但字符串到整型的转换也会让 ICP 失效——MySQL 认为这不是“原始列值比较”。