统计信息 ANALYZE 后执行计划仍不走索引,一般怎么继续排?

【TiDB 使用环境】生产 / 测试都见过
【TiDB 版本】v7.1.x / v7.5.x
【问题描述】
同一 SQL 在 MySQL 走二级索引,迁到 TiDB 后经常全表/错误索引;ANALYZE TABLE 后仍需 USE INDEX 才正常。
想系统化排查:
1)优先看 SHOW STATS_META / 直方图哪些字段?
2)什么情况下该用 binding,而不是长期 hint?
3)有没有常见的「类型转换 / 排序规则」导致索引失效的例子?
求排查清单。

这个只有在迁移测试阶段做好验证监控吧。本身的数据库机制不同,cost计算不同,产生的执行计划就不同。

先看 SHOW STATS_META 的 modify_count/histogram 是否反映真实分布。隐式类型转换(join 列类型不一致)、collation 差异、索引列顺序与查询不匹配是常见走偏原因。binding 用于临时止血,长期改 SQL 或索引列序。

开发搞不定的情况下,比如开发就不配合,或者开发反馈SQL是动态语句,where条件都是动态拼出来的,hint无法适用可能出现的全部排列组合。

1、避免在索引列上进行函数或运算

条件列上如果有函数或计算,将无法走索引,影响性能。在取出字段上可以使用相关函数,但是在 Where 条件中的过滤条件字段上严禁使用任何函数,包括数据类型转换函数。

反例1:

SELECT 字段1
  FROM 表名
 WHERE TRUNC(时间字段) = TRUNC(SYSDATE); 
-- 对时间字段使用TRUNC函数,导致索引失效

正例1:

SELECT 字段1
  FROM 表名
 WHERE 时间字段 >= TRUNC(SYSDATE) 
    AND 时间字段 < TRUNC(SYSDATE) +1

2、避免隐式类型转换

比较值与索引列数据类型要保持一致性,避免数据发生隐式转换。当发生数据类型隐式转换时导致索引不能被使用。应用开发时应根据数据库中字段定义的类型来传值。

常见出现隐式转换的场景:

int字段与字符串做比较
十六进制字段与非数字字段做比较
TIMESTAMP 字段与DATETIME字段比较
number、decimal字段与int字段比较
number、decimal字段与浮点数字段比较

3、避免全模糊、左模糊like匹配

like子句至少保障前端匹配,如写成 ‘like string%’,不要写成 ‘%string%’、 ‘%string’。

正例:

SELECT * FROM city WHERE name LIKE'S% '; --会正确使用索引

反例:

select * from city where name like '%S% ' ; --该语句执行全表扫描```

如果偶尔异常的话,还是怀疑统计信息不稳定,或者是查询谓词越界。
可以贴下查询的SQL语句。

把sql 贴出来,大家可以一起分析。

TiDB 不喜欢的不是二级索引本身,而是 大批量回表操作。当你的 SQL 经过二级索引筛选后仍需读取大量行时,TiDB 认为直接并行扫全表 + Coprocessor下推过滤效率更高。这是分布式数据库面对大量随机读时的理性选择——用计算换IO

TiDB 的优化器成本模型中,对回表操作的 网络延迟惩罚(network latency penalty) 非常重。当 TiDB认为回表代价高时,会倾向选择全表扫描 + 并行处理。

可能TiDB 的统计信息未及时更新,优化器可能 低估了数据量级 或 高估了索引选择性,导致放弃索引

建议按这个排查清单走:

  1. 先看 SHOW STATS_META 检查 modify_count 是否远大于 count,如果差异大说明统计过时;再看 SHOW STATS_HISTOGRAMS 关注 distinct_countnull_countcorrelation,低区分度或高相关性可能导致优化器误判全表扫描更快。

  2. 当统计信息更新后执行计划仍不稳定,或者无法修改 SQL 时用 binding;长期 hint 适合能控制应用代码的场景。

TiDB 依赖异步收集的统计信息(直方图、TopN 等)。如果统计信息不准,或者存在隐式类型转换,TiDB 优化器就极易选错执行计划

绑定一下试试