为什么不建议使用多表join
前言
三年前在一家公司和开发团队的架构师合作过,一起写过代码。让我真的很难受啊,这个架构师写的代码很多逻辑都写到SQL
里面,各种连表查询,SQL
非常的复杂,每次我去维护都得看好久它这个SQL
逻辑。
回到最近,现在有个小伙儿班也是喜欢在SQL里面写逻辑,各种关联查询,甚至写的SQL连一万的数据连都支持不了。
都给我贡献了好几篇文章了:
完了演示的时候报错了!distinct 别乱用啊
sql 子查询的巨坑 ,80%的后端都不知道这个问题
所以我们的SQL
尽量的简洁,少用多表关联查询。
为什么不建议使用多表join?
最主要的原因就是join的效率比较低
MySQL是使用了嵌套循环(Nested-Loop Join)的方式来实现关联查询的,就是要通过两层循环,用第一张表做外循环,第二张表做内循环,外循环的每一条记录跟内循环中的记录作比较,符合条件的就输出。
- 性能问题:
- 多表
JOIN
会增加查询的复杂性,可能导致性能下降,特别是在数据量大时。 - 数据库需要在执行查询时处理更多的行和列,这可能导致更高的 I/O 操作和内存使用。
- 可读性和维护性:
- 复杂的
JOIN
查询会使 SQL 语句变得难以理解,导致维护成本增加。 - 当查询需要频繁修改时,复杂的
JOIN
会让代码更容易出错。
- 索引利用率:
- 多表
JOIN
可能会导致数据库无法有效利用索引,影响查询的优化。 - 如果
JOIN
的字段没有适当的索引,查询性能会显著下降。
- 锁竞争:
- 多表
JOIN
可能导致更长时间的行锁或表锁,从而增加锁竞争的可能性,影响并发性能。
- 数据完整性:
- 复杂的
JOIN
查询可能掩盖数据问题或不一致性,使得调试较为困难。 - 难以确保在
JOIN
查询中返回的数据符合业务逻辑和数据完整性要求。
如何优化:
- 分解查询:在内存中自己做关联,即先从数据库中把数据查出来之后,再次查询,然后再进行数据封装。
- 考虑数据冗余:在某些情况下,可以考虑数据冗余来减少
JOIN
的需要。 - 宽表:就是基于一定的join关系,把数据库中多张表的数据打平做一张大宽表,可以同步到ES或者干脆直接在数据库中直接查都可以
什么是hash join
(扩展阅读)
mysql8.0 以前join查询使用Nested-Loop Join
算法实现
Nested-Loop Join:嵌套循环连接,如果有2张表join的话,复杂度最高是O(n^2),3张表则是O(n^3),表中的数据量越多,JOIN的效率会呈指数级下降。
MySQL 8.0中优化了join查询,新增了 hash join算法。
Hash Join 是一种高效的联表查询算法,通常用于处理较大数据集的连接操作。下面将详细介绍 Hash Join 的原理,并通过示例图解说明其查询步骤。
Hash Join 原理
Hash Join 的基本原理是将一个表的数据构建成一个哈希表,然后利用该哈希表来查找另一个表中匹配的行。其主要分为两个阶段:
- 构建阶段(Build Phase):
- 选择一个较小的表(称为构建表)来创建哈希表。
- 根据连接条件的键值计算哈希值,并将这些键值和对应的行存储在哈希表中。
- 探测阶段(Probe Phase):
- 对另一个表(称为探测表)逐行读取数据。
- 对于探测表中的每一行,计算连接字段的哈希值,并在哈希表中查找匹配的行。
- 如果找到匹配,则将匹配的行组合在一起,形成结果集。
Hash join 案例
假设我们有两个表:
表 A:
ID | Name |
---|---|
1 | Alice |
2 | Bob |
3 | Charlie |
表 B:
ID | Age |
---|---|
1 | 25 |
2 | 30 |
4 | 40 |
我们希望通过 ID 字段将这两个表连接起来。
步骤 1: 构建哈希表
选择表 A 作为构建表。我们将根据 ID 字段创建哈希表。
- 对于 ID = 1,哈希值为
hash(1)
,存储为{1: Alice}
。 - 对于 ID = 2,哈希值为
hash(2)
,存储为{2: Bob}
。 - 对于 ID = 3,哈希值为
hash(3)
,存储为{3: Charlie}
。
哈希表:
{
1: Alice,
2: Bob,
3: Charlie
}
步骤 2: 探测阶段
接下来,我们对表 B 进行探测,查找与哈希表中的行匹配的行。
- 对于 ID = 1,计算
hash(1)
,在哈希表中找到匹配,结果为(1, Alice, 25)
。 - 对于 ID = 2,计算
hash(2)
,在哈希表中找到匹配,结果为(2, Bob, 30)
。 - 对于 ID = 4,计算
hash(4)
,在哈希表中未找到匹配。
匹配之后做聚合就得到结果集了
这里的hash表是存在内存中的,内存是有限制的,超过阈值之后就会走 磁盘Hash join 的算法
磁盘hash join
如果驱动表中的数据量超过阈值,就会走磁盘hash join
的算法。将驱动表拆分成多个哈希区(或桶),每个桶存储在磁盘上。读取磁盘上的hash桶
分别加载到内存,进行探测匹配,探测完成释放当前内存桶,继续从磁盘上读取下一个hash
桶进行探测匹配,直到磁盘上所有的hash
桶都处理完毕。
总结
在实际开发中,尽量减少多表join
查询,保持SQL
的逻辑清晰,这样不仅能提高性能,还有利于维护。
感谢佬们的一键三连+关注 !!!
来源:juejin.cn/post/7438597251487268875