在使用非Equi联接条件时,请避免使用嵌套循环联接


当我在更新查询中使用非等联接条件时,Postgres正在使用嵌套循环联接算法。我知道Nested Loop Join可能非常昂贵,因为根据[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
  1. 在执行上述查询的过程中,是否有任何方法可以避免嵌套循环联接?

  2. 或者有什么方法可以帮助我降低嵌套循环扫描的成本,目前只更新50000条记录需要6-7分钟?

在这种情况下,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;

我知道嵌套循环联接可能非常昂贵,因为在左侧关系中发现的每一行都会扫描右侧关系一次

但是;权利关系";这是一个索引扫描,而不是对整个表的扫描。

您可以通过将联接条件的前导列更改为类似where tgt.col1+0 = stage.col1 ...的内容来停止使用索引。这样做后,它可能会变为散列联接或合并联接,但您必须尝试一下,看看它是否会。此外,新计划实际上可能不会更快。(如果可行的话,修复估计问题会更好(

或者有什么方法可以帮助我降低嵌套循环扫描,目前只需6-7分钟即可更新50000记录?

您的计划显示,超过一半的时间都花在了更新本身上,因此可能只减少嵌套循环扫描的成本对总体时间的影响很小。你桌上有很多索引吗?这些索引的维护可能是一个主要的瓶颈。

最新更新