一个简单的查询执行计划走偏

【 TiDB 使用环境】生产环境
【 TiDB 版本】V7.1.3
【遇到的问题:问题现象及影响】 执行计划异常, 添加了一个过滤条件就变成全表扫描了, hint尝试过 leading no_hash_join use_index force_index 都没用, 表也手动分析过

PS: tt1 主键 OB_OBJECT_ID
tt2 opdate 字段有索引,单独查询可以快速过滤出数据

mysql> explain
    ->   SELECT tt1.*
    ->     FROM wind.tb_object_6757 tt1,
    ->          (select rec_id
    ->             from wind_replicator.wind_tb_object_6757_oplog
    ->            where opdate > date_sub(NOW(), INTERVAL 1 DAY) limit 100) tt2
    ->    where tt1.ob_object_id = tt2.rec_id;
+--------------------------------------+---------+-----------+-----------------------------------------------------------------------------------+
| id                                   | estRows | task      | access object                                                                     |
+--------------------------------------+---------+-----------+-----------------------------------------------------------------------------------+
| IndexJoin_18                         | 100.00  | root      |                                                                                   |
| ├─Projection_39(Build)               | 100.00  | root      |                                                                                   |
| │ └─IndexLookUp_38                   | 100.00  | root      | partition:P202403,P202404                                                         |
| │   ├─Limit_37(Build)                | 100.00  | cop[tikv] |                                                                                   |
| │   │ └─IndexRangeScan_35            | 100.00  | cop[tikv] | table:WIND_TB_OBJECT_6757_OPLOG, index:idx_WINDTBOBJECT6757OPLOG_TIME(OPDATE, ID) |
| │   └─TableRowIDScan_36(Probe)       | 100.00  | cop[tikv] | table:WIND_TB_OBJECT_6757_OPLOG                                                   |
| └─IndexLookUp_17(Probe)              | 100.00  | root      |                                                                                   |
|   ├─IndexRangeScan_15(Build)         | 100.00  | cop[tikv] | table:tt1, index:PRIMARY(OB_OBJECT_ID)                                            |
|   └─TableRowIDScan_16(Probe)         | 100.00  | cop[tikv] | table:tt1                                                                         |
+--------------------------------------+---------+-----------+-----------------------------------------------------------------------------------+
9 rows in set (0.01 sec)

mysql> explain
    ->   SELECT tt1.*
    ->     FROM wind.tb_object_6757 tt1,
    ->          (select rec_id
    ->             from wind_replicator.wind_tb_object_6757_oplog
    ->            where opdate > date_sub(NOW(), INTERVAL 1 DAY) limit 100) tt2
    ->    where tt1.ob_object_id = tt2.rec_id
    ->
    ->      and instr(tt1.f5_6757, 'xxxx') <> 0;
+--------------------------------------+--------------+-----------+-----------------------------------------------------------------------------------+
| id                                   | estRows      | task      | access object                                                                     |
+--------------------------------------+--------------+-----------+-----------------------------------------------------------------------------------+
| HashJoin_17                          | 100.00       | root      |                                                                                   |
| ├─Projection_28(Build)               | 100.00       | root      |                                                                                   |
| │ └─IndexLookUp_27                   | 100.00       | root      | partition:P202403,P202404                                                         |
| │   ├─Limit_26(Build)                | 100.00       | cop[tikv] |                                                                                   |
| │   │ └─IndexRangeScan_24            | 100.00       | cop[tikv] | table:WIND_TB_OBJECT_6757_OPLOG, index:idx_WINDTBOBJECT6757OPLOG_TIME(OPDATE, ID) |
| │   └─TableRowIDScan_25(Probe)       | 100.00       | cop[tikv] | table:WIND_TB_OBJECT_6757_OPLOG                                                   |
| └─Selection_29(Probe)                | 190620123.20 | root      |                                                                                   |
|   └─TableReader_31                   | 238275154.00 | root      |                                                                                   |
|     └─TableFullScan_30               | 238275154.00 | cop[tikv] | table:tt1                                                                         |
+--------------------------------------+--------------+-----------+-----------------------------------------------------------------------------------+
9 rows in set, 1 warning (0.01 sec)

1 个赞

按照文档解释,需要在探查tt1前先根据谓词instr()<>0筛选数据,那就会全表扫描tt1

我这里的tt2 有有效的过滤条件且过滤条件上有索引, 而且tt2表我还只限制了100条记录,且tt1和tt2关联可以走tt1的主键字段ob_object_id, 但他还是选择tt1全表扫描,无法理解

有2个办法
第一条sql作为一个子查询,吧instr()<>0放到外面

或者加 STRAIGHT_JOIN指定join顺序

您说的这两种方法都试了一下,问题照旧。
现在不是谁先谁后的问题,而是他走了hash_join , 这个和谁先谁后关系已经不大了

改成in的子查询呢

最早的时候就是写的 in , 不行才改成join的

看下 SELECT tt1.* from wind.tb_object_6757 tt1 where tt1.ob_object_id=? and instr(f5_6757,‘xxxx’)<>0,是走索引还是全表扫描

ob_object_id 是主键,走的 point_get

问题出在这个条件上 and instr(tt1.f5_6757, ‘xxxx’) <> 0
如果把 条件直接改为 tt1.f5_6757 like ‘%xxxx%’, 执行计划就没问题

你hint指定下tt1和tt2分别走PRIMARY(OB_OBJECT_ID)和:idx_WINDTBOBJECT6757OPLOG_TIME(OPDATE, ID)索引试下

试过了,还是全表扫描

问题出在这个条件上 and instr(tt1.f5_6757, ‘xxxx’) <> 0
如果把 条件直接改为 tt1.f5_6757 like ‘%xxxx%’, 执行计划就没问题

用/*+inl_join(tt1) */ 试一下。

应该是instr无法下推到tikv,导致的tt1表的全表扫,like是可以下推的。。。
https://docs.pingcap.com/zh/tidb/v7.1/expressions-pushed-down#下推到-tikv-的表达式列表

这个的方案测试的时候需要注意下这个会影响全局,需要慎重一点

6.5.7的版本,我对这个执行计划也有疑问。表健康指数值93.但不加hint,就不走索引。加了hint查询毫秒出结果。不加要几秒钟。

你这种情况,把要查询的表手动分析下看看

问题依旧

set @@tidb_enable_outer_join_reorder=off; 然后看下执行计划

继续走全表扫描