TIDB 在 LEFT JOIN 时,为什么不使用被关联表的索引,而是全表扫描? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
pkoukk
V2EX    数据库

TIDB 在 LEFT JOIN 时,为什么不使用被关联表的索引,而是全表扫描?

  •  
  •   pkoukk 221 天前 2353 次点击
    这是一个创建于 221 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有个很简单的查询,唯一的问题是数据表较大,A 有千万级,B 有接近十亿

    SELECT FIELDS FROM A LEFT JOIN B ON A.a=B.b WHERE A.w="" AND A.time>? 

    A 在 time 上有索引,B 表在 b 上有索引
    在我开发环境的 MySQL 中,explain 看上去一切正常

    A range time B ref idx_b 

    但是到了线上的 TIDB 中,就变成了

    IndexRangeScan_101(Build) table:A ,index:idx_time(time) TableFullScan_114 table:B keep order:false 

    主要的问题是,B 变成了全表扫描,完全无法接受。
    如果像 MySQL 正常索引,A 表过滤后的数据量只有几千条。 改了 USE INDEX 以后,变成了索引全扫描?情况也没好到哪里去... Analyze Table 的命令也执行过了,还是没变化

    IndexRangeScan_101(Build) table:A ,index:idx_time(time) IndexFullScan table:B, index:idx_b(b) keep order:false 
    11 条回复    2025-05-13 22:53:03 +08:00
    wuxianliang
        1
    wuxianliang  
       221 天前 via Android
    B 表取的字段全加索引上试试呢?
    winoros
        2
    winoros  
       221 天前 via Android
    最好把 explain 的结果完整的贴一下,可以把表名之类得遮掉。现在这些信息不太能判断问题
    c4pt0r
        3
    c4pt0r  
       221 天前
    @winoros 这位是 TiDB team 的朋友, @pkoukk 麻烦提供一下更多信息,多谢
    Sidney
        4
    Sidney  
       221 天前
    可以先确认一下 A.a = B.b, 这两个列的类型是不是一致的,有没有类型转换
    pkoukk
        5
    pkoukk  
    OP
       221 天前
    @winoros
    替换了表名的 explain 结果,另外上面给的 SQL 有一点小偏差,不过应该也能从 explain 中看出来。
    实际是类似于
    SELECT FIELDS FROM A
    LEFT JOIN B ON A.a=B.b AND B.type="realease" AND B.ip !=""
    WHERE A.w="" AND A.time>?


    ```
    Projection_7 35303209.48 root B.ip, A.ip_id, A.is_deleted, A.insert_time, A.delete_time
    └─HashJoin_26 35303209.48 root left outer join, equal:[eq(A.ip_id, B.ip_id)]
    ├─IndexLookUp_47(Build) 2933525.58 root
    │ ├─IndexRangeScan_44(Build) 3106246.80 cop[tikv] table:A, index:idx_delete_time(delete_time) range:[1740758400,+inf], keep order:false
    │ └─Selection_46(Probe) 2933525.58 cop[tikv] eq(A.is_deleted, 1), lt(A.insert_time, 1743436800)
    │ └─TableRowIDScan_45 3106246.80 cop[tikv] table:A keep order:false
    └─TableReader_50(Probe) 35303209.48 root data:Selection_49
    └─Selection_49 35303209.48 cop[tikv] eq(B.type, "release"), ne(B.ip, ""), not(isnull(B.ip_id))
    └─TableFullScan_48 1163701770.00 cop[tikv] table:B keep order:false
    ```
    winoros
        6
    winoros  
       220 天前 via Android
    这个看起来是估算的问题 A 表的估算是三百万行,这个表是刚刚被 analyze 过吗,如果不是的话可以看一下这个表上次 analyze 的时间
    pkoukk
        7
    pkoukk  
    OP
       220 天前
    @winoros #6 analyze 过了,因为触发了慢查询告警才发现的,运维已经重新跑过 analyze 了
    spkinger
        8
    spkinger  
       220 天前
    盲猜下,换掉不等查询,B.ip !="" 换成 B.ip > "" 试试?
    Defined
        9
    Defined  
       220 天前
    B 表要走 ip_id 相关的 index 的话 join 需要时 index join ,可以加 hint 试下,但这个数量级用 index join 不一定比 hash join 快。

    hint 参考
    EXPLAIN ANALYZE SELECT /*+ INL_JOIN(t1, t2) */ * FROM t1 INNER JOIN t2 ON t1.id = t2.t1_id WHERE t1.int_col = 1;
    winoros
        11
    winoros  
       220 天前
    @winoros https://docs.pingcap.com/zh/tidb/stable/sql-plan-management/
    和 binding 来控制执行计划

    可以的话可以看一下 delete_time > 1740758400 的 [explain analyze]( https://docs.pingcap.com/zh/tidb/stable/sql-statement-explain-analyze/#explain-analyze)(估算行数和真实执行行数)是否接近
    以及单独只有 insert_time > 1743436800 的 explain analyze
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2765 人在线 &nsp; 最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 07:29 PVG 15:29 LAX 23:29 JFK 02:29
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86