Oracle 上的大规模 UPDATE 与 MERGE 性能
Massive UPDATE vs. MERGE performance on Oracle
我正在尝试使用单个大量 UPDATE
语句从源 table 更新目标 table,但执行时间比应有的长得多。
查询
UPDATE MY_DEST
SET (DEST_B, DEST_C) = (
SELECT SRC_A + SRC_B, SRC_B
FROM MY_SRC
WHERE SRC_KEY = DEST_KEY AND SRC_DATE = DEST_DATE
);
两个 table 都包含大约 10 到 1300 万行,它们具有匹配的主键,我们可以安全地假设目标 table 中的每一行在源 table.
Table定义
CREATE TABLE MY_SRC (
SRC_KEY VARCHAR2(50),
SRC_DATE DATE,
SRC_A NUMBER(15,2),
SRC_B NUMBER(15,2),
CONSTRAINT MY_SRC_PK PRIMARY KEY (SRC_KEY, SRC_DATE)
);
CREATE TABLE MY_DEST (
DEST_KEY VARCHAR2(50),
DEST_DATE DATE,
DEST_B NUMBER(15,2),
DEST_C NUMBER(15,2),
CONSTRAINT MY_DEST_PK PRIMARY KEY (DEST_KEY, DEST_DATE)
);
执行计划
Plan hash value: 3904754293
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 12M| 675M| 128M (20)| 01:23:55 |
| 1 | UPDATE | MY_DEST | | | | |
| 2 | TABLE ACCESS FULL | MY_DEST | 12M| 675M| 69756 (1)| 00:00:03 |
| 3 | TABLE ACCESS BY INDEX ROWID| MY_SRC | 1 | 46 | 4 (0)| 00:00:01 |
|* 4 | INDEX UNIQUE SCAN | MY_SRC_PK | 1 | | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("SRC_KEY"=:B1 AND "SRC_DATE"=:B2)
问题
上述 UPDATE
查询是否无可救药地被迫使用缓慢的逐行执行计划?
是否可以只将上面的查询重写为 MERGE
语句来优化,例如下面的语句?
替代查询,重写为 MERGE
MERGE INTO MY_DEST
USING (SELECT SRC_KEY, SRC_DATE, SRC_B, SRC_A + SRC_B AS SRC_C FROM MY_SRC)
ON (DEST_KEY = SRC_KEY AND DEST_DATE = SRC_DATE)
WHEN MATCHED THEN UPDATE SET DEST_B = SRC_B, DEST_C = SRC_C;
备用执行计划
Plan hash value: 2444580570
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | 12M| 638M| | 359K (1)| 00:00:15 |
| 1 | MERGE | MY_DEST | | | | | |
| 2 | VIEW | | | | | | |
|* 3 | HASH JOIN | | 12M| 2260M| 716M| 359K (1)| 00:00:15 |
| 4 | TABLE ACCESS FULL| MY_SRC | 12M| 568M| | 162K (1)| 00:00:07 |
| 5 | TABLE ACCESS FULL| MY_DEST | 12M| 1695M| | 69756 (1)| 00:00:03 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("DEST_KEY"="SRC_KEY" AND "DEST_DATE"="SRC_DATE")
当使用 UPDATE
时,我能否获得替代 MERGE
语句相同的良好性能?
我正在使用 Oracle 12c。
1) 是的,您永远不想在大型 table.
索引块大部分位于磁盘上,因此您可能期望每秒访问 100 行。
即使由于缓存而每秒获得 1000 个索引访问,
您仍然需要等待数小时才能处理 1200 万行。
所以执行计划 1 NO GO 对于大 table。
2) MERGE
的执行计划(具有两次完整 table 扫描的散列连接)看起来不错,应该可以工作。
3) 是的,您可以在设置中毫无问题地使用 UPDATE
语句。您将使用 updatable join view
的功能
查询
update (
select SRC_A, SRC_B,DEST_B, DEST_C
from MY_DEST d
left outer join MY_SRC s
on d.DEST_KEY = s.SRC_KEY and d.DEST_DATE = s.SRC_DATE)
set DEST_B = SRC_A + SRC_B,
DEST_C = SRC_B
;
执行计划
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 1000K| 57M| | 6682 (1)| 00:00:01 |
| 1 | UPDATE | MY_DEST | | | | | |
|* 2 | HASH JOIN OUTER | | 1000K| 57M| 40M| 6682 (1)| 00:00:01 |
| 3 | TABLE ACCESS FULL| MY_DEST | 1000K| 28M| | 1341 (2)| 00:00:01 |
| 4 | TABLE ACCESS FULL| MY_SRC | 1000K| 28M| | 1341 (2)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("D"."DEST_DATE"="S"."SRC_DATE"(+) AND
"D"."DEST_KEY"="S"."SRC_KEY"(+))
您会看到为 MERGE
创建了类似的执行计划,因此您会期待类似的性能。
最后的笔记
您还可以使用并行提示来加速。
别忘了您必须在会话中启用 parallel dml。
ALTER SESSION ENABLE PARALLEL DML;
您的 MERGE
陈述 不等于 您的第一个 UPDATE
陈述。
当目标 table 中存在主键但源 table 中不存在主键时,就会出现差异。
UPDATE
将目标列重置为 NULL
而 MERGE
让它们 不变 .
我的 UPDATE
语句使用 外连接 所以它的行为与你的 UPDATE
- 切换到 内连接 以获得 MERGE
行为。
我正在尝试使用单个大量 UPDATE
语句从源 table 更新目标 table,但执行时间比应有的长得多。
查询
UPDATE MY_DEST
SET (DEST_B, DEST_C) = (
SELECT SRC_A + SRC_B, SRC_B
FROM MY_SRC
WHERE SRC_KEY = DEST_KEY AND SRC_DATE = DEST_DATE
);
两个 table 都包含大约 10 到 1300 万行,它们具有匹配的主键,我们可以安全地假设目标 table 中的每一行在源 table.
Table定义
CREATE TABLE MY_SRC (
SRC_KEY VARCHAR2(50),
SRC_DATE DATE,
SRC_A NUMBER(15,2),
SRC_B NUMBER(15,2),
CONSTRAINT MY_SRC_PK PRIMARY KEY (SRC_KEY, SRC_DATE)
);
CREATE TABLE MY_DEST (
DEST_KEY VARCHAR2(50),
DEST_DATE DATE,
DEST_B NUMBER(15,2),
DEST_C NUMBER(15,2),
CONSTRAINT MY_DEST_PK PRIMARY KEY (DEST_KEY, DEST_DATE)
);
执行计划
Plan hash value: 3904754293
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 12M| 675M| 128M (20)| 01:23:55 |
| 1 | UPDATE | MY_DEST | | | | |
| 2 | TABLE ACCESS FULL | MY_DEST | 12M| 675M| 69756 (1)| 00:00:03 |
| 3 | TABLE ACCESS BY INDEX ROWID| MY_SRC | 1 | 46 | 4 (0)| 00:00:01 |
|* 4 | INDEX UNIQUE SCAN | MY_SRC_PK | 1 | | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("SRC_KEY"=:B1 AND "SRC_DATE"=:B2)
问题
上述
UPDATE
查询是否无可救药地被迫使用缓慢的逐行执行计划?是否可以只将上面的查询重写为
MERGE
语句来优化,例如下面的语句?替代查询,重写为
MERGE
MERGE INTO MY_DEST USING (SELECT SRC_KEY, SRC_DATE, SRC_B, SRC_A + SRC_B AS SRC_C FROM MY_SRC) ON (DEST_KEY = SRC_KEY AND DEST_DATE = SRC_DATE) WHEN MATCHED THEN UPDATE SET DEST_B = SRC_B, DEST_C = SRC_C;
备用执行计划
Plan hash value: 2444580570 ---------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | ---------------------------------------------------------------------------------------- | 0 | MERGE STATEMENT | | 12M| 638M| | 359K (1)| 00:00:15 | | 1 | MERGE | MY_DEST | | | | | | | 2 | VIEW | | | | | | | |* 3 | HASH JOIN | | 12M| 2260M| 716M| 359K (1)| 00:00:15 | | 4 | TABLE ACCESS FULL| MY_SRC | 12M| 568M| | 162K (1)| 00:00:07 | | 5 | TABLE ACCESS FULL| MY_DEST | 12M| 1695M| | 69756 (1)| 00:00:03 | ---------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("DEST_KEY"="SRC_KEY" AND "DEST_DATE"="SRC_DATE")
当使用
UPDATE
时,我能否获得替代MERGE
语句相同的良好性能?
我正在使用 Oracle 12c。
1) 是的,您永远不想在大型 table.
索引块大部分位于磁盘上,因此您可能期望每秒访问 100 行。
即使由于缓存而每秒获得 1000 个索引访问, 您仍然需要等待数小时才能处理 1200 万行。
所以执行计划 1 NO GO 对于大 table。
2) MERGE
的执行计划(具有两次完整 table 扫描的散列连接)看起来不错,应该可以工作。
3) 是的,您可以在设置中毫无问题地使用 UPDATE
语句。您将使用 updatable join view
查询
update (
select SRC_A, SRC_B,DEST_B, DEST_C
from MY_DEST d
left outer join MY_SRC s
on d.DEST_KEY = s.SRC_KEY and d.DEST_DATE = s.SRC_DATE)
set DEST_B = SRC_A + SRC_B,
DEST_C = SRC_B
;
执行计划
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 1000K| 57M| | 6682 (1)| 00:00:01 |
| 1 | UPDATE | MY_DEST | | | | | |
|* 2 | HASH JOIN OUTER | | 1000K| 57M| 40M| 6682 (1)| 00:00:01 |
| 3 | TABLE ACCESS FULL| MY_DEST | 1000K| 28M| | 1341 (2)| 00:00:01 |
| 4 | TABLE ACCESS FULL| MY_SRC | 1000K| 28M| | 1341 (2)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("D"."DEST_DATE"="S"."SRC_DATE"(+) AND
"D"."DEST_KEY"="S"."SRC_KEY"(+))
您会看到为 MERGE
创建了类似的执行计划,因此您会期待类似的性能。
最后的笔记
您还可以使用并行提示来加速。
别忘了您必须在会话中启用 parallel dml。
ALTER SESSION ENABLE PARALLEL DML;
您的 MERGE
陈述 不等于 您的第一个 UPDATE
陈述。
当目标 table 中存在主键但源 table 中不存在主键时,就会出现差异。
UPDATE
将目标列重置为 NULL
而 MERGE
让它们 不变 .
我的 UPDATE
语句使用 外连接 所以它的行为与你的 UPDATE
- 切换到 内连接 以获得 MERGE
行为。