对于普通的表,我们一直说truncate速度是非常快的,基本秒级能够完成,但是对于海量数据表(例如几十亿行的大表)就不是这么回事了。之前truncate一个20亿行左右的表执行了超过半个小时,网上搜索到了更好的方法,记录学习一下。
一、 大表直接删除主要问题
- 系统内存资源和CPU资源的使用升高。集中进行drop操作,系统会进行数据表结构的回收,对应数据分区和数据块的回收。当数据表很大的时候,这个过程自然很长,对CPU来说消耗时间和数量都很大。
- 临时段空间消耗巨大。因为在drop table的过程中,会将数据表转移到临时段进行处理。
- 对应用的影响。期间业务不可用,且难以预估所需时间。
二、 分步删除大表
总的指导原则是先删除数据,不影响数据一致性的要求,之后分阶段分步骤的进行空间回收,避免一次性drop对系统造成过大的压力。
- 使用带reuse storage子句的truncate table,将数据删除。reuse storage子句的作用是单纯降低数据段data segment的高水位线,对分配的空间不进行回收。这样truncate操作不涉及到空间回收,速度是可以接受的。
- 分若干次数,使用deallocate unused keep XXX的方法,将分配的空间回收。keep后面可以加入维持空间数量,所以可以分若干个窗口期进行回收。
准备20万的数据,远谈不上海量,示例而已。
SQL> select count(*) from m;
COUNT(*)
----------
201600
此时,我们分析作为一个data segment,数据表M的相关参数信息。可以知道,数据表M共分配在37个exetents上,共包括2816个数据块。空间为23M左右。
SQL> select header_file, header_block, blocks, bytes, extents from dba_segments where segment_name='M' and wner='SYS';
HEADER_FILE HEADER_BLOCK BLOCKS BYTES EXTENTS
----------- ------------ ---------- ---------- ----------
1 62353 2816 23068672 37
删除实验,测试直接使用truncate table
SQL> select count(*) from md;
COUNT(*)
----------
201604
Executed in 0.451 seconds
SQL> truncate table md;
Table truncated
Executed in 0.901 seconds
测试使用reuse storage子句,reuse storage子句下,空间是不进行回收的,所以相对速度较快。在dba_segments上,依然显示37个exetents的空间分配。
SQL> truncate table m reuse storage;
Table truncated
Executed in 0.52 seconds
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
Executed in 0.43 seconds
SQL> select header_file, header_block, blocks, bytes, extents from dba_segments where segment_name='M' and wner='SYS';
HEADER_FILE HEADER_BLOCK BLOCKS BYTES EXTENTS
----------- ------------ ---------- ---------- ----------
1 62353 2816 23068672 37
Executed in 0.131 seconds
分阶段进行空间回收,让回收工作在受控范围下进行。
SQL> alter table m deallocate unused keep 20M; --控制在20M
Table altered
Executed in 0.11 seconds
SQL> select header_file, header_block, blocks, bytes, extents from dba_segments where segment_name='M' and wner='SYS';
HEADER_FILE HEADER_BLOCK BLOCKS BYTES EXTENTS
----------- ------------ ---------- ---------- ----------
1 62353 2568 21037056 36
Executed in 0.12 seconds
可见空间降低到20M左右。此后,可以根据自身情况不断进行回收空间工作。
SQL> alter table m deallocate unused keep 15M;
Table altered
Executed in 0.06 seconds
SQL> alter table m deallocate unused keep 3M;
Table altered
Executed in 0.01 seconds
SQL> alter table m deallocate unused keep 100k;
Table altered
Executed in 0.01 seconds
SQL> select header_file, header_block, blocks, bytes, extents from dba_segments where segment_name='M' and wner='SYS';
HEADER_FILE HEADER_BLOCK BLOCKS BYTES EXTENTS
----------- ------------ ---------- ---------- ----------
1 62353 16 131072 2
Executed in 0.03 seconds
变小后就好处理了,直接drop table即可。
SQL> drop table m;
Table dropped
Executed in 0.06 seconds
三、 结论
写SQL的时候一定要注意操作的数据量和范围。对于可能存在的海量数据访问,一定要实现有准备,有监控,这样才能保证系统可用性以及变更成功率
海量数据处理的一个方法就是分割,根据业务的限制和要求,在时间上进行分割。处理原则是避开业务系统繁忙时间段,尽量将高负载工作进行划分实现。
一般来说,进行drop数据表是不能中间终止的。drop数据表时,oracle首先把数据段转化为临时段对象,之后开始不断的进行空间回收。即使这个过程中间强制终止,再次启动的时候smon进程也会进行回收删除操作。所以,对数据表进行直接drop的时候,切记三思。