苦逼,下班了,遇到一个 SQL 查询慢的问题,和同事讨论很久,结果用代码解决了

2020-04-01 20:04:39 +08:00
 stevenkang

情况是这样的,mysql 5.7.14-log,有两个表,A 表 2000w 条数据,B 表 1000w 条数据。

现有需求:select a.x, b.x from A a left join B b on a.bid = b.id where a.time >= xxx and b.time <= xxx limit 0, 100

a.bid 、a.time 有索引的情况下,查询一次 5000ms 左右,查询频繁了 10000ms+ 导致接口超时。

和同事讨论了很久,各种 sql 子查询什么的都很慢。最后想到用 in 的方式程序拼接 sql 语句。

于是用了下面的流程:

select a.x, a.bid from A a where a.time >= xxx and b.time <= xxx limit 0, 100

程序将上面查询的 a.bid 拼装为 sql: select b.id, b.x from B b where b.id in (xx1, xx2...xx100)

然后程序中用 b.id 作为 key,b.x 作为 value 建立映射

再次循环查询 A 的数据,将 b.x 通过程序设置进去,然后统一返回。

目前这种方案查询下来 50ms,不知道有什么隐患没。

10295 次点击
所在节点    MySQL
57 条回复
m1ch3ng
2020-04-02 12:20:04 +08:00
单独查 a 是 index 级别,单独查 b 是 range 级别。个人感觉 left join 好一点,因为连接查询是 eq_ref 级别,比单独查 b 的 range 级别好,而且少了一次请求
reus
2020-04-02 15:25:15 +08:00
恭喜你,你发明了 hash join 。

msyql 8 有 hash join 了,不要用 5 了。
l00t
2020-04-02 15:30:37 +08:00
你这语句就是错的……

还是贴执行计划吧
krixaar
2020-04-02 15:47:08 +08:00
如果需求是 A 表取 100 条,B 表如果有就带上,没有就没有,那么第一条语句 where b.time <= xxx 限定 b.time 必须有值,left join 就没有 left 的效果了吧?
stevenkang
2020-04-02 16:11:12 +08:00
@reus 老系统,一线码农,没有办法让用 mysql 8

@l00t 抱歉之前笔误 a.time 打成了 b.time,已经附加了执行计划

@m1ch3ng 之前也是用的 left join 直接查,索引也加上了,测试环境没问题,结果上了生产查询很慢
@TZ a.time 的范围不会超过一天,数据大概 7w 左右
themostlazyman
2020-04-02 16:13:34 +08:00
on 后面的连接条件可以把 where 的晒选提前:SELECT a.x, b.x FROM A a LEFT JOIN B b ON a.bid = b.id AND a.time >= xxx AND b.time <= xxx limit 0, 100
kim01
2020-04-02 16:44:57 +08:00
对于这种有数据量的表,单表读取不香吗,尽量单表操作然后再组合数据,就是以后拆分也简单快捷不好吗。。
themostlazyman
2020-04-02 17:43:24 +08:00
@themostlazyman
再加个 WHERE a.time >= xxx
TZ
2020-04-02 18:28:16 +08:00
你的执行计划太诡异了,你 ref 是 func,难道你还用了函数?
TZ
2020-04-02 18:37:41 +08:00
我就算模拟了你这种 func,我 a 表 800 万,b 表 400 万,查询也才 100ms 左右。
https://i.loli.net/2020/04/02/ATPIOFkZlWip6gs.png
RipL
2020-04-02 19:14:12 +08:00
@TZ in 不走索引这个不一定吧
RipL
2020-04-02 19:20:37 +08:00
@TZ 你这个用了索引覆盖 还用了 mrr
RipL
2020-04-02 19:21:25 +08:00
@raysonlu 这个可以试试
TZ
2020-04-02 19:39:52 +08:00
@RipL 嗯,不一定。哈哈哈哈,因为我这个数据里面的时间是跟 id 单调递增的,反而关了 mrr 性能更好。索引覆盖?我的 a.x,b.x 怎么被覆盖的。
https://i.loli.net/2020/04/02/S7OLH231A6tQGNs.png
RipL
2020-04-02 20:46:44 +08:00
@TZ 逃~ 看错了,是索引下推。看楼主的第二个索引比较长,200 多。
stevenkang
2020-04-03 09:51:52 +08:00
@TZ 查询的时候只是简单的 left join,没有用到函数,检查了一下字段,发现 a.bid 长度为 255,b.id 长度为 50,不知道是否因为这个原因导致的索引 ref 为 func
zwj2885
2020-04-03 10:01:40 +08:00
搞大数据搞的习惯,如果业务场景是实时查询的,那就把表做分区,或者像你这样,把计算过程放到 java 中。如果是实时要求高,就放到 kafka 这类里面进行计算。如果离线分析就好办了。

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

https://www.v2ex.com/t/658434

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

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

© 2021 V2EX