我管理一个非常大的应用程序(一个表格中有超过5亿行的数据)Oracle数据库后端。该数据库并没有真正做任何事情(没有sprocs,没有触发器或任何东西),而只是一个数据存储。

每个月,我们都需要从两个主表中清除记录。清除的标准各不相同,是行年龄和几个状态字段的组合。我们通常每月清除10到5000万行(通过进口量增加约3-5百万行)。

目前,我们必须在大约50,000行(即删除50000,COMIT,DELETE 50000,COMPL,REPER)中进行此删除。尝试一次删除整个批次,使数据库无响应约一个小时(取决于行#)。在系统上删除排行的行很粗糙,我们通常必须在一周的时间内按照时间允许的方式进行操作;允许脚本连续运行可以导致用户无法接受的性能降解。

我认为,这种批次删除也会降低索引性能,并具有其他影响,最终导致数据库的性能降解。仅一个表上只有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;

此数据库 必须 上涨99.99999%,我们每年只有2天的维护窗口。

我正在寻找一种更好的方法来删除这些记录,但是我还没有找到任何记录。有什么建议么?

有帮助吗?

解决方案

具有“ 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;

其他提示

对此的经典解决方案是 分割 您的桌子,例如按月或每周。如果您以前没有遇到过它们,则一张分区表就像几个具有隐式的相同结构化表 UNION 选择时,根据分区标准插入时,Oracle将在适当的分区中自动存储一行。您提到索引 - 每个分区也都有自己的分区索引。放弃分区是甲骨文中非常便宜的操作(类似于一个 TRUNCATE 在负载方面,因为这是您真正在做的事情 - 截断或丢弃这些看不见的子表之一)。 “事实之后”进行分区将是大量的处理,但是对溢出的牛奶没有任何感觉 - 到目前为止,要做的牛奶的优势大于成本。每个月,您都会将最高分区分开,以创建下个月数据的新分区(您可以轻松地使用A自动化THS DBMS_JOB).

借助分区,您也可以利用 并行查询消除分区, ,这应该使您的用户非常高兴...

要考虑的一个方面是,索引的删除性能中有多少以及从原始表中产生了多少。从表中删除的每个记录都需要从每个BTREE索引对行的相同删除。如果您有30多个BTREE索引,我怀疑您的大部分时间都花在索引维护上。

这对分区的实用性有影响。说你的名字有索引。一个标准的BTREE索引,一方面,可能必须进行四个跳跃才能从根部块到叶子块,并进行第五次读取以获取行。如果该索引分为50个段,并且您没有作为查询的一部分的分区密钥,则需要检查50个段中的每个段。每个细分市场都会较小,因此您可能只需要进行2个跳跃,但最终可能会进行100次读取,而不是前5次。

如果它们是位图索引,则方程式不同。您可能不是使用索引来识别单个行,而是使用索引。因此,它不是使用5个iOS返回单个记录的查询,而是使用10,000 ios。因此,该索引的额外分区中的额外开销并不重要。

每月50,000批次的删除5000万张记录仅为1000次迭代。如果您每30分钟删除1次,则应满足您的要求。计划运行您发布的查询但删除循环的计划任务,因此仅执行一次,因此不会对用户造成明显的降级。我们在制造工厂中的记录量大约相同,几乎可以满足我们的需求。实际上,我们每10分钟将其散布更多10,000个记录,在我们的Oracle Unix服务器上运行大约1或2秒钟。

如果磁盘空间不高,您可以创建表格的“工作”副本,说 my_table_new, ,使用CTA(创建表作为SELECT)使用标准,以省略要删除的记录。您可以并行执行创建语句,并带有附加提示以使其快速,然后构建所有索引。然后,一旦完成(经过测试),将现有表命名为 my_table_old 并将“工作”表重命名为 my_table. 。一旦您对一切感到满意 drop my_table_old purge 摆脱旧桌子。如果有一堆外键约束,请看一下 dbms_redefinition PL/SQL软件包. 。使用适当的选项时,它将克隆您的索引,相反等。这是汤姆·凯特(Tom Kyte)的建议的总结 Asktom 名望。第一次运行后,您可以自动化所有内容,并且创建表应该更快地进行,并且可以在系统启动时完成,并且应用停机时间将仅限于不到一分钟的时间来重命名桌子。使用CTA要比进行多个批次删除的速度要快得多。如果您没有获得许可的分区,这种方法可能特别有用。

示例CTA,将行与最近365天的数据保持在一起,并 flag_inactive = 'N':

create /*+ append */ table my_table_new 
   tablespace data as
   select /*+ parallel */ * from my_table 
       where some_date >= sysdate -365 
       and flag_inactive = 'N';

-- test out my_table_new. then if all is well:

alter table my_table rename to my_table_old;
alter table my_table_new rename to my_table;
-- test some more
drop table my_table_old purge;

当丢弃分区时,您将不可用的全球索引,需要重建,重建全球索引将是一个大问题,好像您在线上这样做一样,它会很慢,否则您需要停机时间。无论哪种情况,都不适合这一要求。

“我们通常每月清除10到5000万行””

我建议使用PL/SQL批次删除,我认为可以使用几个小时。

许可以下: CC-BY-SA归因
不隶属于 dba.stackexchange
scroll top