TiDB优化器选择问题

【 TiDB 使用环境】生产环境
【 TiDB 版本】6.5.7,
【遇到的问题】
最基本的sql。优化器选择的执行计划有问题。导致查询慢。

sql语句:
select * from t1 where create_time >= ‘2024-03-01 00:00:00’ limit 2;

这个表create_time是有索引的。但查询时默认并不走索引。

mysql> explain select * from t1 where create_time >=‘2024-03-01 00:00:00’ limit 2;
±-----------------------------±--------±----------±------------------±-----------------------------------------------------------------+
| id | estRows | task | access object | operator info |
±-----------------------------±--------±----------±------------------±-----------------------------------------------------------------+
| Limit_8 | 2.00 | root | | offset:0, count:2 |
| └─TableReader_13 | 2.00 | root | | data:Limit_12 |
| └─Limit_12 | 2.00 | cop[tikv] | | offset:0, count:2 |
| └─Selection_11 | 2.00 | cop[tikv] | | ge(t1.weplay_user.create_time, 2024-03-01 00:00:00.000000) |
| └─TableFullScan_10 | 21.50 | cop[tikv] | table:weplay_user | keep order:false |
±-----------------------------±--------±----------±------------------±-----------------------------------------------------------------+
5 rows in set (0.00 sec)

mysql> explain select * from t1 use index(idx_create_time) where create_time >=‘2024-03-01 00:00:00’ limit 2;
±-------------------------------±--------±----------±------------------------------------------------------±---------------------------------------------------+
| id | estRows | task | access object | operator info |
±-------------------------------±--------±----------±------------------------------------------------------±---------------------------------------------------+
| IndexLookUp_13 | 2.00 | root | | limit embedded(offset:0, count:2) |
| ├─Limit_12(Build) | 2.00 | cop[tikv] | | offset:0, count:2 |
| │ └─IndexRangeScan_10 | 2.00 | cop[tikv] | table:t1, index:idx_create_time(create_time) | range:[2024-03-01 00:00:00,+inf], keep order:false |
| └─TableRowIDScan_11(Probe) | 2.00 | cop[tikv] | table:t1 | keep order:false |
±-------------------------------±--------±----------±------------------------------------------------------±---------------------------------------------------+

手工分析表试试analyze table
另外表数据量多少条,你这个语句实际返回多少条,从estRows 是不是数据条数太少

收集统计信息

在mysql执行也会出现这种情况,要么统计信息不准确,要么表数据量比较大

你这个表数据量是多少,前面的是它评估在21.5行之内就能找到 >=‘2024-03-01 00:00:00’ 的2条数据。

1.收集下统计信息看看
2.表是不是很小,总数据量太小导致完全没必要走索引?

表的健康度有关吧,我遇到过一开始sql很快,后来突然慢了很多,查了半天是执行计划变了,选择了一个慢的索引,是表健康度太低导致的。

其实很正常,你这个应该是数据有大量倾斜导致的,所以优化器判断全表扫描21行的代价就能找到 >=‘2024-03-01 00:00:00’ 的数据。如果优化器判断我要扫描百万才能找到 >=‘2024-03-01 00:00:00’ 的数据它就会走索引。所以收集一下统计信息,让直方图和topN正确应该就能正常。

:thinking:这个表是不是数据量非常小?如果数据量特别小的话,会直接走全表扫描的。

1 个赞

翔哥说的对 :+1:

这是个1000多万的表。不走索引要5秒左右。强制走idx_create_time索引,查询耗时是毫秒级的。

新建的表不?

:thinking:那可能就是楼上说的这种情况了

1 个赞

按大佬们说的,收个统计信息,再试

analyze table后,如果确定过滤条件有INDEX【必须是前缀】,先count总条目数,然后在根据过滤条件查出count,如果行数差异>15%,那优化器会认为全表扫描更快

你去掉 limit 2; 试试