Cod*_*lla 19 oracle delete oracle-11g
我管理一个应用程序,它有一个非常大的(近 1TB 的数据,一个表中有超过 5 亿行)Oracle 数据库后端。数据库并没有真正做任何事情(没有 SProcs,没有触发器或任何东西)它只是一个数据存储。
每个月我们都需要从两个主表中清除记录。清除的标准各不相同,是行年龄和几个状态字段的组合。我们通常最终每月清除 10 到 5000 万行(我们每周通过导入增加大约 3 到 500 万行)。
目前我们必须分批进行大约 50,000 行的删除(即删除 50000、提交、删除 50000、提交、重复)。尝试一次删除整个批次会使数据库在大约一个小时内没有响应(取决于行数)。像这样批量删除行在系统上是非常粗糙的,我们通常必须“在时间允许的情况下”在一周内完成;允许脚本连续运行会导致用户无法接受的性能下降。
我认为这种批量删除也会降低索引性能,并有其他影响最终导致数据库性能下降。一张表就有34个索引,索引的数据量实际上比数据本身还大。
这是我们的一位 IT 人员用来执行此清除操作的脚本:
BEGIN
LOOP
delete FROM tbl_raw
where dist_event_date < to_date('[date]','mm/dd/yyyy') and rownum < 50000;
exit when SQL%rowcount < 49999;
commit;
END LOOP;
commit;
END;
Run Code Online (Sandbox Code Playgroud)
该数据库必须达到 99.99999%,而且我们每年只有 2 天的维护窗口。
我正在寻找一种更好的方法来删除这些记录,但我还没有找到。有什么建议?
Ren*_*ger 18
'A' 和 'B' 的逻辑可能“隐藏”在一个虚拟列后面,您可以在该列上进行分区:
alter session set nls_date_format = 'yyyy-mm-dd';
drop table tq84_partitioned_table;
create table tq84_partitioned_table (
status varchar2(1) not null check (status in ('A', 'B')),
date_a date not null,
date_b date not null,
date_too_old date as
( case status
when 'A' then add_months(date_a, -7*12)
when 'B' then date_b
end
) virtual,
data varchar2(100)
)
partition by range (date_too_old)
(
partition p_before_2000_10 values less than (date '2000-10-01'),
partition p_before_2000_11 values less than (date '2000-11-01'),
partition p_before_2000_12 values less than (date '2000-12-01'),
--
partition p_before_2001_01 values less than (date '2001-01-01'),
partition p_before_2001_02 values less than (date '2001-02-01'),
partition p_before_2001_03 values less than (date '2001-03-01'),
partition p_before_2001_04 values less than (date '2001-04-01'),
partition p_before_2001_05 values less than (date '2001-05-01'),
partition p_before_2001_06 values less than (date '2001-06-01'),
-- and so on and so forth..
partition p_ values less than (maxvalue)
);
insert into tq84_partitioned_table (status, date_a, date_b, data) values
('B', date '2008-04-14', date '2000-05-17',
'B and 2000-05-17 is older than 10 yrs, must be deleted');
insert into tq84_partitioned_table (status, date_a, date_b, data) values
('B', date '1999-09-19', date '2004-02-12',
'B and 2004-02-12 is younger than 10 yrs, must be kept');
insert into tq84_partitioned_table (status, date_a, date_b, data) values
('A', date '2000-06-16', date '2010-01-01',
'A and 2000-06-16 is older than 3 yrs, must be deleted');
insert into tq84_partitioned_table (status, date_a, date_b, data) values
('A', date '2009-06-09', date '1999-08-28',
'A and 2009-06-09 is younger than 3 yrs, must be kept');
select * from tq84_partitioned_table order by date_too_old;
-- drop partitions older than 10 or 3 years, respectively:
alter table tq84_partitioned_table drop partition p_before_2000_10;
alter table tq84_partitioned_table drop partition p_before_2000_11;
alter table tq84_partitioned_table drop partition p2000_12;
select * from tq84_partitioned_table order by date_too_old;
Run Code Online (Sandbox Code Playgroud)
Gai*_*ius 14
对此的经典解决方案是对您的表进行分区,例如按月或按周。如果你以前没有遇到过,一个分区表就像是几个结构相同的表,UNION
选择时隐式,Oracle会根据分区条件自动在插入时将行存储在适当的分区中。您提到了索引 - 每个分区也都有自己的分区索引。在 Oracle 中删除分区是一种非常便宜的操作(类似于TRUNCATE
在负载方面,因为这就是您真正在做的事情 - 截断或删除这些不可见的子表之一)。“事后”进行分区将是大量的处理,但是为打翻的牛奶哭泣是没有意义的——到目前为止这样做的好处超过了成本。每个月,您都会拆分顶部分区,为下个月的数据创建一个新分区(您可以使用 轻松实现自动化DBMS_JOB
)。
使用分区,您还可以利用并行查询和分区消除,这应该会让您的用户非常高兴......