Nested Loop Join 就是扫描一个表(外表),每读到该表中的一条记录,就去“扫描”另一张表(内表)找到满足条件的数据。这里的“扫描”可以是利用索引快速定位扫描,也可以是全表扫描。通常来说,全表扫描的性能是很差的,所以如果连接条件的列上没有索引,优化器一般就不会选择 Nested Loop Join。在 OceanBase 中,计划中展示了是否能够利用索引快速定位扫描。

    如下例所示,第一个计划对于内表的扫描是全表扫描,因为连接条件是 t1.c = t2.c,而 t2 没有在 c 上面的索引。第二个计划对于内表的扫描能够使用索引快速找到匹配的行,主要原因是因为连接条件是 t1.b = t2.b, 而且 t2 选择了创建在 b列上的索引 k1 作为访问路径,这样的话对于 t1 中的每一行的每个 b 值,t2 都可以根据索引快速找到满足条件的匹配行。

    Nested Loop Join 的一个优化变种是 Blocked Nested Loop Join,它的区别在于每个从外表中读取一个 Block 大小的行,然后再去扫描内表找到满足条件的数据。这样的一个好处是可以减少内表的读取次数。

    Nested Loop Join 通常在内表行数比较少,而且外表在连接条件的列上有索引的时候会比较好,因为内表中的每一行都可以快速的使用索引定位到相对应的匹配的数据。

    在多对多的两张表上进行 Merge时,通常需要使用临时空间进行操作。例如 A join B 使用 Merge Join 时,如果对于关联字段的某一组值,在 A 和 B 中都存在多条记录 A1、A2…An、B1、B2…Bn,则为A中每一条记录 A1、A2…An,都必须在 B 中对所有相等的记录 B1、B2…Bn 进行一次匹配。这样,指针需要多次从 B1 移动到 Bn,每一次都需要读取相应的 B1…Bn 记录。将 B1…Bn 的记录预先读出来放入内存临时表中,比从原数据页或磁盘读取要快。在一些场景中,如果连接字段上有可用的索引,并且排序一致,那么可以直接跳过排序操作。

    通常来说,Merge join 比较适合两个输入表已经有序的情况,否则 Hash Join 会更加好。下图展示了两个 Merge join 的计划,其中第一个是需要排序的,第二个是不需要排序的(因为两种表都选择了 k1 这两个索引访问路径,这两个索引本身就是按照 b 排序的)。

    如下是Hash Join计划的示例。