在使用非 Equi 连接条件时避免使用嵌套循环连接
Posted
技术标签:
【中文标题】在使用非 Equi 连接条件时避免使用嵌套循环连接【英文标题】:Avoid using Nested Loop Join while using a non Equi join condition 【发布时间】:2020-11-02 15:24:53 【问题描述】:当我在更新查询中使用非 equi 连接条件时,Postgres 使用的是嵌套循环连接算法。我知道嵌套循环连接可能非常昂贵,因为根据左关系中找到的每一行扫描一次右关系 [https://www.postgresql.org/docs/8.3/planner-optimizer.html]
更新查询和执行计划如下。
查询
explain analyze
UPDATE target_tbl tgt
set descr = stage.descr,
prod_name = stage.prod_name,
item_name = stage.item_name,
url = stage.url,
col1_name = stage.col1_name,
col2_name = stage.col2_name,
col3_name = stage.col3_name,
col4_name = stage.col4_name,
col5_name = stage.col5_name,
col6_name = stage.col6_name,
col7_name = stage.col7_name,
col8_name = stage.col8_name,
flag = stage.flag
from tbl1 stage
where tgt.col1 = stage.col1
and tgt.col2 = stage.col2
and coalesce(tgt.col3, 'col3'::text) = coalesce(stage.col3, 'col3'::text)
and coalesce(tgt.col4, 'col4'::text) = coalesce(stage.col4, 'col4'::text)
and stage.row_number::int >= 1::int
and stage.row_number::int < 50001::int;
执行计划
Update on target_tbl tgt (cost=0.56..3557.91 rows=1 width=813) (actual time=346153.460..346153.460 rows=0 loops=1)
-> Nested Loop (cost=0.56..3557.91 rows=1 width=813) (actual time=4.326..163876.029 rows=50000 loops=1)
-> Seq Scan on tbl1 stage (cost=0.00..2680.96 rows=102 width=759) (actual time=3.060..2588.745 rows=50000 loops=1)
Filter: (((row_number)::integer >= 1) AND ((row_number)::integer < 50001))
-> Index Scan using tbl_idx on target_tbl tgt (cost=0.56..8.59 rows=1 width=134) (actual time=3.152..3.212 rows=1 loops=50000)
Index Cond: ((col1 = stage.col1) AND (col2 = stage.col2) AND (COALESCE(col3, 'col3'::text) = COALESCE(stage.col3, 'col3'::text)) AND (COALESCE(col4, 'col4'::text) = COALESCE(stage.col4, 'col4'::text)))
Planning time: 17.700 ms
Execution time: 346157.168 ms
有什么办法可以避免上述查询执行过程中的嵌套循环连接?
或者有什么方法可以帮助我降低嵌套循环扫描的成本,目前仅更新 50000 条记录需要 6-7 分钟?
【问题讨论】:
你有tbl(col1, col2)
的索引吗?
No @a_horse_with_no_name 目前我只有 col1, col2, COALESCE(col3, 'col3'::text), COALESCE(col4, 'col4'::text) 上的 target_tbl 索引
【参考方案1】:
在这种情况下,PostgreSQL 可以选择不同的连接策略。没有的原因是顺序扫描中的严重错误估计:102 而不是 50000。
解决这个问题,事情就会变得更好:
ANALYZE tbl1;
如果这还不够,请收集更详细的统计数据:
ALTER TABLE tbl1 ALTER row_number SET STATISTICS 1000;
ANALYZE tbl1;
所有这些都假设row_number
是一个整数并且类型转换是多余的。如果您错误地使用了不同的数据类型,那么索引是您唯一的希望:
CREATE INDEX ON tbl1 ((row_number::integer));
ANALYZE tbl1;
【讨论】:
非常感谢@Laurenz Albe。在 row_number 列上创建索引已将时间减少到 2 分钟【参考方案2】:我知道嵌套循环连接可能非常昂贵,因为在左侧关系中找到的每一行都会扫描一次右侧关系
但这里的“正确关系”是索引扫描,而不是全表扫描。
您可以通过将连接条件的前导列更改为where tgt.col1+0 = stage.col1 ...
之类的内容来停止使用索引。这样做后,它可能会更改为哈希联接或合并联接,但是您必须尝试一下,看看是否可以。此外,新计划实际上可能不会更快。 (如果可行的话,修复估计问题会更好)
或者有什么方法可以帮助我降低成本 嵌套循环扫描,目前只需 6-7 分钟即可更新 50000 记录?
您的计划表明,超过一半的时间花在更新本身上,因此可能仅降低嵌套循环扫描的成本对总时间的影响很小。表上有很多索引吗?这些索引的维护可能是一个主要瓶颈。
【讨论】:
非常感谢@jjanes 的帮助,你是对的,目标表上有很多索引,但不幸的是我没有删除或重建它们的权限。以上是关于在使用非 Equi 连接条件时避免使用嵌套循环连接的主要内容,如果未能解决你的问题,请参考以下文章