使用大表时提高 MERGE 性能
Posted
技术标签:
【中文标题】使用大表时提高 MERGE 性能【英文标题】:Improve MERGE performance when using big tables 【发布时间】:2016-12-27 19:10:12 【问题描述】:上下文
我们有一个模型,其中每个element
都有一个element kind
和从0 到N 的features
。每个feature
只属于一个element
,并且有一个feature name
。
这被建模为下表:
ELEMENTS
elem_id int not null -- PK
elem_elki_id int not null -- FK to ELEMENT_KINDS
-- more columns with elements data
ELEMENT_KINDS
elki_id int not null -- PK
-- more columns with elements kinds data
FEATURES
feat_id int not null -- PK
feat_elem_id int not null -- FK to ELEMENTS
feat_fena_id int not null -- FK to FEATURE_NAMES
-- more columns with features data
FEATURE_NAMES
fena_id int not null -- PK
-- more columns with feature_names data
要求
有一个用feature kinds
表替换feature names
表的新要求。
每个 (element kind, feature name)
对都有一个(而且只有一个)feature kind
。
模型的变化是添加一个新列并创建一个新表:
ALTER TABLE features ADD feat_feki_id int null;
CREATE TABLE FEATURE_KINDS
(
feki_id int not null, -- PK
feki_elki_id int not null, -- FK to ELEMENT_KINDS
feki_fena_id int null, -- FK* to FEATURE_NAMES
-- more columns with feature kinds data
)
*feki_fena_id
实际上是一个临时列,显示哪个feature name
用于创建每个feature kind
。填充feat_feki_id
后,feki_fena_id
应与feat_fena_id
和feature names
表一起被丢弃。
问题
成功填充features kinds
表后,我们尝试使用以下查询填充feat_feki_id
列:
MERGE INTO features F
USING
(
SELECT *
FROM elements
INNER JOIN feature_kinds
ON elem_elki_id = feki_elki_id
) EFK
ON
(
F.feat_elem_id = EFK.elem_id AND
F.feat_fena_id = EFK.feki_fena_id
)
WHEN MATCHED THEN
UPDATE SET F.feat_feki_id = EFK.feki_id;
这适用于带有测试数据的小案例场景,但在生产中我们有 ~2000 万 elements
和 ~2000 feature_kinds
,大约需要一个小时在抛出 ORA-30036: unable to extend segment by 8 in undo tablespace 'UNDOTBS1' 错误之前。
问题
有什么方法可以提高MERGE
的性能以使其正常工作吗? (也许我缺少一些索引?)
是否有其他替代方法可以填充feat_feki_id
列? (我们已经尝试过UPDATE
而不是MERGE
,结果相似)
【问题讨论】:
所以让段更大,或者被一些具有一组范围值的字段分解 请您在分手部分详细说明一下好吗? (扩大细分市场似乎不是一种选择......仍然需要一个多小时才能出现问题) 【参考方案1】:目前尚不清楚是否发生了问题,或者您的撤消段是否太小。你可以在没有得到 ORA-30036 的情况下执行以下语句吗?
UPDATE features f SET f.feat_feki_id = 12345;
如果这不起作用,您只需要增加撤消段的大小。 Kludges 可以分块进行更新,但你真的不应该这样做。
假设这不是一个简单的 UNDO 大小问题,您可能会做的一件事是确保您的 MERGE
(或 UPDATE
)按照它们在表中出现的顺序更新行。否则,您可能会一遍又一遍地重新访问相同的块,这确实会损害性能并增加 UNDO 的使用率。我在几年前不得不做的类似手术中遇到了这个问题,当我终于弄明白时我感到很震惊。
为了避免我遇到的问题,你需要这样的东西:
MERGE INTO features F
USING
(
SELECT f.feat_id, fk.feki_id
FROM features f
INNER JOIN elements e ON e.elem_id = f.feat_elem_id
INNER JOIN feature_kinds fk ON fk.feki_elki_id = e.elem_elki_id and fk.feki_fena_id = f.feat_fena_id
-- Order by the ROWID of the table you are updating to ensure you are not revisiting the same block over and over
ORDER BY f.rowid
) EFK
ON
(
F.feat_id = efk.feat_id )
)
WHEN MATCHED THEN
UPDATE SET F.feat_feki_id = EFK.feki_id;
我可能弄错了您的数据模型,但关键是在MERGE
查询和ORDER BY features.rowid
中包含FEATURES
表,以确保更新按行顺序进行。
【讨论】:
谢谢,我会尽可能测试一下。即使我遇到了 UNDO 表空间问题,耗时一个多小时的查询似乎也有问题。目前,我担心features
表几乎与elements
表一样大,因此您建议的双重连接可能会导致一个巨大的表。你认为这会是个问题吗?
是的,这可能是个问题。连接两个 20M 行表(加上第三个较小的表)并对结果进行排序是很费力的。如果可以的话,使用并行查询来提供帮助。查询计划应该涉及大量的全扫描和散列连接,因此您应该能够在运行时查询V$SESSION_LONGOPS
以查看查询的进展情况。
此解决方案有效,但ORDER BY
导致TEMP
表空间的大量使用。就我而言,大约 50 GB。为了让它工作,我必须为该表空间创建一个附加文件,正如这个答案所示:***.com/a/25350967
感谢您的反馈!我想知道是否有办法编写查询,使其自然按features.rowid
排序,如果是这样,依赖这种行为是否安全。我在想可能是一个提示,使FEATURES
成为散列连接中的所谓“探针表”而不是“构建表”。如果您需要更快地获得它和/或摆脱 50GB 临时使用量,可能值得进一步调查。
至于性能,整个查询在不到一个小时的时间内运行,并且不应该如此频繁地运行,因此您当前的解决方案可以解决问题。我会考虑您关于避免订购的提示的其他建议。以上是关于使用大表时提高 MERGE 性能的主要内容,如果未能解决你的问题,请参考以下文章