在使用非 Equi 连接条件时避免使用 Nested Loop Join
Avoid using Nested Loop Join while using a non Equi join condition
当我在更新查询中使用非相等连接条件时,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
有什么办法可以避免上面查询执行过程中的嵌套循环连接?
或者有什么方法可以帮助我降低嵌套循环扫描的成本,目前仅更新 50000 条记录需要 6-7 分钟?
在这种情况下,PostgreSQL 可以选择不同的连接策略。之所以没有,是因为顺序扫描中的毛mis-estimate: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;
I understand that the Nested Loop Join can be very costly as the right relation is scanned once for every row found in the left relation
但是这里的“右关系”是索引扫描,而不是全table的扫描。
您可以通过将连接条件的前导列更改为类似 where tgt.col1+0 = stage.col1 ...
的内容来使其停止使用索引。执行此操作后,它可能会更改为散列连接或合并连接,但您必须尝试一下,看看是否可以。此外,新计划实际上可能不会更快。 (如果可行的话,最好解决估计问题)
Or is there a way that can help me to reduce the cost of the the
nested loop scan, currently it takes 6-7 minutes to update just 50000
records?
您的计划表明超过一半的时间花在了更新本身上,因此降低嵌套循环扫描的成本可能对总时间的影响很小。 table 上有很多索引吗?这些索引的维护可能是一个主要瓶颈。
当我在更新查询中使用非相等连接条件时,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
有什么办法可以避免上面查询执行过程中的嵌套循环连接?
或者有什么方法可以帮助我降低嵌套循环扫描的成本,目前仅更新 50000 条记录需要 6-7 分钟?
在这种情况下,PostgreSQL 可以选择不同的连接策略。之所以没有,是因为顺序扫描中的毛mis-estimate: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;
I understand that the Nested Loop Join can be very costly as the right relation is scanned once for every row found in the left relation
但是这里的“右关系”是索引扫描,而不是全table的扫描。
您可以通过将连接条件的前导列更改为类似 where tgt.col1+0 = stage.col1 ...
的内容来使其停止使用索引。执行此操作后,它可能会更改为散列连接或合并连接,但您必须尝试一下,看看是否可以。此外,新计划实际上可能不会更快。 (如果可行的话,最好解决估计问题)
Or is there a way that can help me to reduce the cost of the the nested loop scan, currently it takes 6-7 minutes to update just 50000 records?
您的计划表明超过一半的时间花在了更新本身上,因此降低嵌套循环扫描的成本可能对总时间的影响很小。 table 上有很多索引吗?这些索引的维护可能是一个主要瓶颈。