在没有并行提示和批量收集的情况下删除数百万条记录

Posted

技术标签:

【中文标题】在没有并行提示和批量收集的情况下删除数百万条记录【英文标题】:Deletion of millions of records without parallel hint and bulk collect 【发布时间】:2016-08-13 05:55:56 【问题描述】:

我有一个表 PROD_MAIN,它在一个数据库中包含 7.5 亿条记录。数据库基础设施非常基础,没有任何 RAC。它只是 1 个数据库。 要求是删除超过 1 年的记录。我编写了一个带有并行提示和批量收集的 PL SQL 代码。执行需要很长时间。请在下面找到代码。

ALTER SESSION ENABLE PARALLEL DML;

DECLARE
TYPE TABLE_DELETE IS TABLE OF ROWID;
T_DELETE TABLE_DELETE;
CURSOR C_DELETE IS
SELECT /*+ PARALLEL(10) */ ROWID FROM PROD_MAIN WHERE RECORD_DATE < (TRUNC(SYSDATE) - 366);
L_DELETE_BUFFER PLS_INTEGER := 50000;
BEGIN
OPEN C_DELETE;
LOOP
FETCH C_DELETE BULK COLLECT
INTO T_DELETE LIMIT L_DELETE_BUFFER;
FORALL I IN 1..T_DELETE.COUNT
DELETE /*+ PARALLEL(10) */ PROD_MAIN WHERE ROWID = T_DELETE(I);
EXIT WHEN C_DELETE%NOTFOUND;
COMMIT;
END LOOP;
CLOSE C_DELETE;
COMMIT;
END;

ALTER SESSION DISABLE PARALLEL DML;

我也在桌子上做了 NOLOGGING。我创建了索引并进行了统计数据收集,但性能没有提高。那么,有没有其他方法可以在 3 - 5 小时内删除数百万条记录?

【问题讨论】:

您要删除几部分的行? @Justin Cave :我正在删除超过 1 年的行。勾选后,需要删除的记录总数为 7.5 亿中的 4 亿。 好的。你有几个小时的停机时间?桌子上有触发器吗?这是外键约束中的父表还是子表? 此表上没有触发器。但是,我通过创建一个新的重复表来解决它,该表的值大于 366 天并截断了主表。并重命名了表格。 【参考方案1】:

如果表是按日期分区的,你可以截断超过一年的分区(截断分区不需要时间,不会降级)

如果它没有分区,我认为你能做的最好的事情就是不要尝试在一个事务中删除所有记录。尝试删除一些记录并将其放入循环中。例如,我想删除 10.000 条记录,您可以这样做:

DELETE FROM your_table WHERE your_conditions LIMIT 10.000 (mysql)
DELETE FROM your_table WHERE your_conditions AND rownum <10000 (Oracle)

记住在完成后优化表(甚至在删除之间交替),因为它会降低索引。

根据您的环境要求,您可以尝试的另一件事是创建一个空表副本,然后从 SELECT 执行 INSERT,在新表中插入您要维护的所有行。之后,截断原始表,删除它,然后重命名新表。

MyOriginalTable whit All Data
Create en Empty Copy: MyTemporalTable (without indexes)
Move valid data from MyOriginalTable to MyTemporalTable
Truncate and Drop MyOriginalTable
Create indexes in MyTemporalTable
Rename MyTemporalTable to MyORiginalTable

【讨论】:

如何使用日期对现有表 PROD_MAIN 进行分区?这对我来说是一个新概念。数据库管理会这样做还是 PL SQL 开发人员会这样做?我正在使用 Oracle DB。另外,我需要在 2 到 3 小时内删除至少 4 亿条记录 我认为分区应该由 dba 完成,但是创建具有如此大量数据的分区将花费大量时间。如果你能做第二个选项,也许是最好的。只要记住在没有索引的情况下创建表副本并在填充表时创建它们。 +1 for option 2 ,创建一个新表,这是最有效的方法。 asktom.oracle.com/pls/apex/… 分区是企业版许可证之上的一项额外费用选项。如果有人将他们的数据库基础架构描述为“非常基本的”,那么他们似乎不太可能拥有企业版和额外的成本分区选项。批量删除几乎肯定会比运行删除所有数据的单个delete 语句要慢。如果目标是删除 7.5 亿行中的 4 亿行,并且您有停机时间窗口,那么创建一个新表并移动您想要保留的数据是合理的。 @Justin Cave 你是对的,但是在 mysql 等其他 rdbms 中它是免费的,我试图帮助任何有类似问题的人,即使他们没有使用 oracle 系统【参考方案2】:

我认为问题是:此表是其他表的主表。

加快禁用其他表中的那些外键。然后删除行,然后启用索引。

但是“Diego Sal Diaz”的第三个解决方案是将剩余行复制到临时表并重命名它也很好。

【讨论】:

【参考方案3】:

我通过创建一个临时表 PROD_MAIN_TEMP 解决了这个问题,该表具有与 PROD_MAIN 一样的精确表结构。创建后,我插入了我想要保留的数据。 SELECT /*+ PARALLEL(10) */ * FROM PROD_MAIN WHERE RECORD_DATE

【讨论】:

“Diego Sal Diaz”提出了这个解决方案。

以上是关于在没有并行提示和批量收集的情况下删除数百万条记录的主要内容,如果未能解决你的问题,请参考以下文章

删除数百万条记录 oracle [关闭]

如何从 db2 表中删除数百万条记录

使用 hive sql 批量插入数百万条记录到 hive?

数据库如何删除数百万条记录?直接sql语句delete?服务器一直在跑,数据库也在变化 我要删除部分数据

mysql进阶 十五 mysql批量删除大量数据

需要帮助优化涉及数百万条记录的非常慢的 DB2 SQL 查询