为啥 PL/SQL Bulk DML 对具有父子约束表的大型数据集运行缓慢?
Posted
技术标签:
【中文标题】为啥 PL/SQL Bulk DML 对具有父子约束表的大型数据集运行缓慢?【英文标题】:Why is PL/SQL Bulk DML running slowing for large datasets with parent-child constrained tables?为什么 PL/SQL Bulk DML 对具有父子约束表的大型数据集运行缓慢? 【发布时间】:2011-07-13 18:02:12 【问题描述】:我一直试图弄清楚为什么对于记录表有几十万或更多记录的数据集,这个 PL/SQL 清除脚本运行缓慢。在脚本执行之前,记录表的某个子集被标记为清除 - 大约 75%。
是什么导致 Record_Part 表的删除比其他表的删除时间长这么多?是不是因为它位于 3-table 父子层次结构的中间?我是否在索引或约束方面错过了一些知识?我可以做些什么来加快这个定期清除过程?
这是一个 Oracle 10g 数据库。
提前感谢您阅读我的问题。
架构(部分):
记录表是父表 Record_Part 表是 Record 的子表(Record 有很多 Record_Part) Record_Person 是 Record_Part 的子项(Record_Part 有多个 Record_Person) 典型比率为 1:7:9(记录:记录部分:记录人)记录
PK - sysid 物理标识 待定 purge_in_progressRecord_Part
PK - Part_pk FK - record_sysidRecord_Person
PK - sysid FK - Part_pk运行时
50000 条记录条目
record_person forall 在 1:40 分钟内完成 record_part forall 在 1:20 分钟内完成 记录在 10 秒内完成300000 条记录条目
record_person forall 在 9 分钟内完成 record_part forall 在 2 小时内完成 记录在 20 分钟内完成2000000 条记录
record_person forall 在 1 内完成 小时 record_part forall 在 13 内完成 小时 (!) 记录在 8 分钟内完成索引和约束 DDL
alter table Record add constraint record_REC_PK primary key (SYSID) using index tablespace DB_INDEX1;
alter table Record_Part add constraint RECPART_REC_PK primary key (Part_PK) using index tablespace DB_INDEX1;
alter table Record_Part add constraint RECPART_FK foreign key (RECORD_SYSID) references record (SYSID);
alter table Record_Person add constraint RECPERSON_REC_PK primary key (SYSID) using index tablespace DB_INDEX1;
alter table Record_Person add constraint RECPERSON_FK foreign key (Part_PK) references Record_Part (Part_PK);
CREATE INDEX REC_PURGE_IDX ON record (PURGE_IN_PROGRESS);
CREATE INDEX REC_PHYSID_IDX ON record (PHYSICALID);
CREATE INDEX REC_PENDING_IDX ON record (PENDING);
CREATE INDEX RECPART_RECORD_SYSID_IDX ON Record_Part (RECORD_SYSID);
CREATE INDEX RECPERSON_PARTPK_IDX on Record_Person (PART_PK);
脚本: (下面的脚本省略了时间戳打印)
DECLARE
TYPE sSysid IS TABLE OF record.sysid%TYPE
INDEX BY PLS_INTEGER;
TYPE physicalid IS TABLE OF record.physicalid%TYPE
INDEX BY PLS_INTEGER;
l_sid sSysid;
l_physicalid physicalid;
BEGIN
SELECT sysid, physicalid
BULK COLLECT INTO l_sid, l_physicalid
FROM record
where purge_in_progress = 1;
FORALL i IN l_sid.FIRST .. l_sid.LAST
delete from record_person where Part_pk like concat(l_sid(i), '%') or Part_pk like concat(l_physicalid(i), '%');
commit;
FORALL i IN l_sid.FIRST .. l_sid.LAST
delete from record_Part where record_sysid = l_sid(i);
commit;
FORALL i IN l_sid.FIRST .. l_sid.LAST
delete from record where sysid = l_sid(i);
END;
/
commit;
【问题讨论】:
您真的是一次将 200 万个元素收集到一个集合中,而不是每次都使用 LIMIT 来获取较少数量的元素吗?您是否跟踪会话以查看时间花费在哪里?如果是这样,您可以发布 tkprof 输出吗? 谢谢贾斯汀。我对批量收集的 rownum 限制运行脚本测试以获取运行时,但我想我没有考虑或意识到收集大小对执行速度的重要性。 不要使用 rownum 限制。在 BULK COLLECT 中使用 LIMIT 子句。 谢谢你,贾斯汀。您对提交频率的评论也很有帮助。 【参考方案1】:查看此主题的第一个回复。正如 Justin 所指出的,您需要使用 limit 子句来获取固定数量的记录(通常使用 100 条,您可以对其进行参数化,看看哪种方式适合您的情况)。
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::p11_question_id:5918938803188
【讨论】:
感谢 Rajesh,我现在正在运行测试,其中有一个 while 循环围绕具有 rownum 限制的批量收集。问题 - 我应该多久提交一次?每 100 个,每 1000 个,还是仅在 while 循环之后一次?在此脚本的上下文中,提交率如何影响速度?谢谢! @kg - 提交只会减慢您的处理速度。仅在您完成逻辑事务时提交。我认为这意味着在您的情况下,除非您有特定的理由不这样做,否则您只会在流程结束时提交一次。【参考方案2】:您能否在测试环境中禁用 FK 约束以查看是否有帮助?
另一种可能性是将 FK 约束重新创建为可延迟的,并在脚本开头延迟约束,例如:
alter table Record_Part
add constraint RECPART_FK foreign key (RECORD_SYSID)
references record (SYSID) DEFERRABLE;
alter table Record_Person
add constraint RECPERSON_FK foreign key (Part_PK)
references Record_Part (Part_PK) DEFERRABLE;
SET CONSTRAINTS ALL DEFERRED;
...run your purge
SET CONSTRAINTS ALL IMMEDIATE;
请注意,任何 COMMIT 都会导致立即设置约束。您必须在每次提交后重新发出第一个设置约束语句。
约束将是我在这里的第一个嫌疑人。
【讨论】:
由于清除脚本将与使用这些表的应用程序同时运行,因此我需要始终保持引用完整性。不过,我确实很欣赏有关延迟约束的信息,这对我将来很有用。谢谢。 可延迟约束仅适用于会话 - 您不会在其他会话中丢失约束检查。 谢谢,我也不知道这个!我将运行一个测试,看看延迟约束是否会加快脚本运行时间。以上是关于为啥 PL/SQL Bulk DML 对具有父子约束表的大型数据集运行缓慢?的主要内容,如果未能解决你的问题,请参考以下文章
带有 BULK COLLECT 的 Oracle PL/SQL 6504