如何从oracle中并行删除记录table

How to delete records in parallel from oracle table

我们正在 table 'application_audit' 中维护对我们应用程序的审核。
我正在尝试编写存储过程以从此 table 中删除我们不再需要的记录。
到目前为止,我已经写了下面的存储过程,但我发现当要删除的行数超过100k 时,它会花费很多时间。

能否帮我实现并行会话或优化以下存储过程中的删除查询以加快执行速度。

在生产中,这个 table 在任何给定时间点至少有 500 万行,据我所知,如果我们每天执行这个存储过程,那么将会有至少要删除 100k 条记录。

在下面的查询中,COMPONENT_NAME='REQUESTPURGE' 意味着对于那个特定的请求号清除已经发生,并且在我们的活动数据库实例中没有请求数据存在于该请求编号,因此 'application_audit' table 中具有该请求编号的所有记录都符合删除条件。

存储过程:

create or replace PROCEDURE APPLICATION_AUDIT_PURGE_RECORD
IS
purgewait number := 30;
BEGIN
  DBMS_OUTPUT.PUT_LINE('Application audit purge started with purge wait value as   '||purgewait||' days');
  delete from application_audit where id in (select id from application_audit where request_number in (select request_number from application_audit where COMPONENT_NAME='REQUESTPURGE' and trunc(timestamp) < trunc(sysdate - purgewait)));

END APPLICATION_AUDIT_PURGE_RECORD;

Table:

  CREATE TABLE "APPLICATION_AUDIT" (    
    "ID" NUMBER GENERATED ALWAYS AS IDENTITY NOT NULL, 
    "MESSAGE_TYPE" VARCHAR2(64 CHAR), 
    "COMPONENT_NAME" VARCHAR2(64 CHAR), 
    "USERNAME" VARCHAR2(32 CHAR), 
    "TIMESTAMP" TIMESTAMP (6) WITH TIME ZONE NOT NULL, 
    "REQUEST_NUMBER" VARCHAR2(64 CHAR), 
    "MODULE_NAME" VARCHAR2(256 CHAR), 
    "PROCESS_NAME" VARCHAR2(256 CHAR), 
    "VERSION" VARCHAR2(64 CHAR), 
    "TASK" VARCHAR2(64 CHAR), 
    "ERROR_CODE" VARCHAR2(256 CHAR), 
    "ERROR_MESSAGE" VARCHAR2(4000 CHAR), 
    "MESSAGE" VARCHAR2(4000 CHAR)
   )

编辑1: 更改存储过程中的删除语句并使用索引显着减少了执行时间。

更新了存储过程中的删除语句:

DELETE FROM APPLICATION_AUDIT WHERE REQUEST_NUMBER IN (SELECT APPLICATION_AUDIT.REQUEST_NUMBER FROM APPLICATION_AUDIT WHERE APPLICATION_AUDIT.REQUEST_NUMBER != 'null' AND APPLICATION_AUDIT.MESSAGE_TYPE='INFO' AND APPLICATION_AUDIT.COMPONENT_NAME='REQUESTPURGE' AND APPLICATION_AUDIT.TASK='DeleteRequest' AND TRUNC(APPLICATION_AUDIT.TIMESTAMP) < TRUNC(SYSDATE - v_reqnumpurgewait));
DELETE FROM APPLICATION_AUDIT WHERE REQUEST_NUMBER = 'null' AND TRUNC(APPLICATION_AUDIT.TIMESTAMP) < TRUNC(SYSDATE - v_purgewait);

索引创建查询:

CREATE INDEX APPLICATION_AUDIT_IDX1 ON APPLICATION_AUDIT (COMPONENT_NAME, TIMESTAMP, (NVL(REQUEST_NUMBER,'null')));
CREATE INDEX APPLICATION_AUDIT_IDX2 ON APPLICATION_AUDIT (NVL(REQUEST_NUMBER,'null'));

我认为您的 DELETE 查询可以简化为 -

DELETE FROM application_audit 
 WHERE COMPONENT_NAME = 'REQUESTPURGE' 
   AND TRUNC(timestamp) < TRUNC(SYSDATE - purgewait);

您也可以尝试在 COMPONENT_NAME 列上建立索引。

删除 500 万条记录应该不会那么耗时。

话虽如此,您可以尝试在 DELETE 语句中添加并行提示。

首先启用


ALTER SESSION ENABLE PARALLEL DML;

如果这没有帮助,您可以查看:

禁用 table

上的索引

但是,当然,在删除运行时,任何需要和使用这些索引的查询都会变慢。所以你只是用一个缓慢的陈述换取(很多)其他陈述。之后你必须重建它们,这将花费(可能是很长的)时间。

您可以通过 SQL 或 rowid

查看分块

如果这些 none 足够有用,您可能需要研究更激进的解决方案。

比如把你想保存的数据保存在一个临时的table。然后删除当前的 table 并重命名临时的。例如:

create table tmp as select ...data you want to keep... from old_tab;
drop old_tab;
rename tmp to old_tab;
-- run grants, indexes etc. that were on the original table
...

但是您需要中断才能执行此操作。

我建议首先使用解释计划或跟踪来追踪瓶颈发生的位置,因为如果 500 万次删除花费很长时间,这听起来像是您有潜在问题

我发现只要找到一行 component_name = 'REQUESTPURGE' 就可以删除具有相同请求编号的所有行。这意味着 component_name 本身并不能告诉我们是否删除一行。否则我建议在这里使用 table 分区。

目前,我能想到的就是提供适当的索引。不过,首先,您的查询可以简化为:

delete from application_audit 
where request_number in 
(
  select request_number 
  from application_audit 
  where component_name = 'REQUESTPURGE' 
  and timestamp < trunc(sysdate - purgewait)
);

我为这个声明建议的索引:

create index idx1 on application_audit (component_name, timestamp, request_number);
create index idx2 on application_audit (request_number);