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

2 天前
 pkoukk

有个很简单的查询,唯一的问题是数据表较大,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
1255 次点击
所在节点    数据库
11 条回复
wuxianliang
2 天前
B 表取的字段全加索引上试试呢?
winoros
2 天前
最好把 explain 的结果完整的贴一下,可以把表名之类得遮掉。现在这些信息不太能判断问题
c4pt0r
2 天前
@winoros 👈 这位是 TiDB team 的朋友, @pkoukk 麻烦提供一下更多信息,多谢
Sidney
2 天前
可以先确认一下 A.a = B.b, 这两个列的类型是不是一致的,有没有类型转换
pkoukk
2 天前
@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
2 天前
这个看起来是估算的问题 A 表的估算是三百万行,这个表是刚刚被 analyze 过吗,如果不是的话可以看一下这个表上次 analyze 的时间
pkoukk
2 天前
@winoros #6 analyze 过了,因为触发了慢查询告警才发现的,运维已经重新跑过 analyze 了
spkinger
2 天前
盲猜下,换掉不等查询,B.ip !="" 换成 B.ip > "" 试试?
Defined
1 天前
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
1 天前
winoros
1 天前
@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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://yangjunhui.monster/t/1131388

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX