掘金 后端 ( ) • 2024-07-01 18:05

关系型数据库一个至关重要的概念就是表连接(JOIN)。本文将介绍表连接的分类、表连接的原理是什么、如何优化表连接。

表连接的分类

表连接的类别可以参考下面这张经典的文氏图,虽然文氏图不能完美地反映表连接的本质,但是可以看出不同表连接的区别。

image.png

表连接可以分为以下几类:

  • 内连接:相当于查询 A、B 交集部分数据
  • 外连接:
    • 左外连接:查询左表所有数据,以及两张表交集部分数据
    • 右外连接:查询右表所有数据,以及两张表交集部分数据
  • 自连接:当前表与自身的连接查询,自连接必须使用表别名

表连接的原理是什么?

MySQL 底层用了基于嵌套循环连接的算法,在两个表都是全表扫描的情况下,将驱动表进行扫描,每查出一条数据就在被驱动表进行查询。也就是说,如果在驱动表中查出了 m 条数据,就会进行 m 次被驱动表的访问,如果在被驱动表中查找出了 n 条数据,时间复杂度就是 O(m*n),相当于使用了两层 for 循环。这是最简单最质朴的方法。

c70114bb129a602514b7a6579dd7a26a_processed.jpg

如何优化表连接?

  1. 使用索引加快连接速度。在数据表上加索引会加快查询速度。这会有个问题,如果想提高性能,是大表做驱动表还是小表做驱动表? 答案是将小表作为驱动表,并在大表上建立索引。由于小表一定需要进行全表扫描,在大表上建立索引可以减少访问次数,提高连接效率。

  2. 基于块的嵌套循环连接算法。当被驱动表很大且内存不足以同时容纳整张表时,会发生频繁的内存替换操作,导致大量的I/O操作,进而减缓速度。我们可以使用连接缓冲区(Join Buffer)。连接缓冲区是在执行连接查询前申请的一块固定大小的内存,先将若干条驱动表结果集中的记录装入其中,然后扫描被驱动表。每条被驱动表的记录一次性与连接缓冲区中的多条驱动表记录进行匹配。由于匹配过程在内存中完成,这样可以显著减少被驱动表的I/O代价。需要注意的是,连接缓冲区中不会存放驱动表记录的所有列,只存放查询列表中的列和过滤条件中的列,因此最好不要使用 * 作为查询列表。 336e5be50d25b93017f4bb06a6d069d3.jpg

  3. 在应用层进行优化。为了提高查询速度,可以在应用层使用冗余存储数据,减少数据库查询次数。例如,可以将数据冗余存储在不同的表中,或者通过调用两次 Service,在应用层拼接数据。此外,可以使用 Elasticsearch 进行聚合查询,提升查询性能。

参考内容:

  1. 《MySQL是怎样运行的》