【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 的统计信息未及时更新,优化器可能 低估了数据量级 或 高估了索引选择性,导致放弃索引
建议按这个排查清单走:
-
先看
SHOW STATS_META检查modify_count是否远大于count,如果差异大说明统计过时;再看SHOW STATS_HISTOGRAMS关注distinct_count、null_count和correlation,低区分度或高相关性可能导致优化器误判全表扫描更快。 -
当统计信息更新后执行计划仍不稳定,或者无法修改 SQL 时用 binding;长期 hint 适合能控制应用代码的场景。
TiDB 依赖异步收集的统计信息(直方图、TopN 等)。如果统计信息不准,或者存在隐式类型转换,TiDB 优化器就极易选错执行计划
绑定一下试试