我们拥有一个成熟的Oracle数据库应用程序(已投入生产超过10年),在此期间,我们一直在使用我们自己设计的脚本来删除不再需要的旧数据。它们通过在频繁提交的循环中针对适当的表发出delete语句来工作,以避免使用i / o或使用过多的撤消空间来使系统过载。
在大多数情况下,它们的工作正常。它们每天运行,从系统中删除最早的数据需要大约一个小时。我所关注的主要问题是对所有这些删除可能具有的表和索引的影响,以及尽管它们不会过度加载系统,但在短时间内删除一天的数据确实会产生影响实例缓冲区缓存,导致后续查询在接下来的几个小时内运行稍慢,因为缓存逐渐恢复。
多年来我们一直在考虑更好的方法。在过去,我听说人们使用分区表来管理旧数据收获 - 例如,每个分区一个月,并且每月删除最旧的分区。这种方法的主要缺点是我们的收获规则超出了“删除月X”。用户可以根据键值指定数据在系统中必须保留多长时间(例如,在发票表中,帐户foo可以在3个月后删除,但帐户栏可能需要保留2年)。
还存在参照完整性问题; Oracle文档讨论了使用分区来主要在数据仓库的上下文中清除数据,其中表往往是超立方体。我们更接近OLTP的结尾,并且月X中的数据与月Y中的数据有关系是很常见的。为这些表创建正确的分区键最多也是痒。
至于缓存井喷,我已经阅读了一些关于设置专用缓冲区缓存的内容,但它似乎更多地基于每个表,而不是基于每个用户或每个事务。为了保留缓存,我非常希望收获工作能够随时在缓存中保留一个事务的数据,因为删除后不需要保留数据。
我们是否在可预见的未来停止使用删除,还是有其他更聪明的方法来处理收割?
答案 0 :(得分:4)
在大多数情况下,我认为你被删除了。
您对案例中使用分区的难度的评论可能会阻止它们被有效使用(根据记录类型使用不同的删除日期)但是您可以创建一个“删除日期”列。您可以分区的记录?它的缺点是更新非常昂贵,因为删除日期的更改可能会导致行迁移,因此您的更新实际上将实现为删除和插入。
即使这样,由于参照完整性问题,您也不能使用DDL分区操作来删除旧数据,但是分区仍然可能用于物理地集群要删除的行,以便需要修改更少的块。为了删除它们,减轻对缓冲区缓存的影响。
答案 1 :(得分:0)
删除并不是那么糟糕,只要您重建索引。 Oracle将恢复不再包含数据的页面。
但是,作为8i(很可能仍然如此),它将无法正确恢复不再包含有效引用的索引页。更糟糕的是,由于索引离开了链接,你可能会陷入一种情况,即它会开始走叶子节点以找到一行。这会导致性能显着下降:通常需要几秒钟的查询可能需要几分钟。下降也是非常突然的:有一天它会好的,第二天它就不会。
我发现了这种行为(因为它存在Oracle错误,所以其他人也有这种行为),其中一个应用程序使用增加的密钥并定期删除数据。我们的解决方案是反转键的一部分,但这不会帮助你使用日期。
答案 2 :(得分:0)
如果暂时停用索引,执行删除然后重建它们会怎样?它会改善删除的性能吗?当然,在这种情况下,您必须确保脚本正确并确保正确的删除顺序和参照完整性。
答案 3 :(得分:0)
我们遇到同样的问题,使用相同的策略。 如果情况变得非常糟糕(索引,表格的分配非常分散......),我们会尝试应用空间回收行动。
表必须允许行移动(如闪回): alter table TTT启用行移动; 改变表TTT收缩空间; 然后重建所有索引。
我不知道你是如何使用维护窗口的,如果应用程序必须一直可用,那就更难了,如果没有,你可以在脱机时进行一些“重新包装”。 “alter table TTT move tablespace SSSS”在重写表时会做很多工作来清理混乱。您还可以指定新的存储参数,例如范围管理,大小,...查看文档。
我使用这样的脚本为整个数据库创建脚本:
SET SQLPROMPT "-- "
SET ECHO OFF
SET NEWPAGE 0
SET SPACE 0
SET PAGESIZE 0
SET FEEDBACK OFF
SET HEADING OFF
SET TRIMSPOOL ON
SET TERMOUT OFF
SET VERIFY OFF
SET TAB OFF
spool doit.sql
select 'prompt Enabling row movement in '||table_name||'...'||CHR (10)||'alter table '||table_name||' enable row movement;' from user_tables where table_name not like '%$%' and table_name not like '%QTAB' and table_name not like 'SYS_%';
select 'prompt Setting initial ext for '||table_name||'...'||CHR (10)||'alter table '||table_name||' move storage (initial 1m);' from user_tables where table_name not like '%$%' and table_name not like '%QTAB' and table_name not like 'SYS_%';
select 'prompt Shrinking space for '||table_name||'...'||CHR (10)||'alter table '||table_name||' shrink space;' from user_tables where table_name not like '%$%' and table_name not like '%QTAB' and table_name not like 'SYS_%';
select 'prompt Rebuilding index '||index_name||'...'||CHR (10)||'alter index '||index_name||' rebuild;' from user_indexes where status = 'UNUSABLE';
spool off
prompt now check and then run @doit.sql
exit