costdown遇到了大量数据清理的需求,整理下基本思路及方法

一、 哪些表是大表

1. 按空间大小

包含CLOB大小但不含索引大小,如果库很大,全库统计会比较耗时,可以增加并行或过滤条件,分批处理。

SELECT owner,object_name,SUM(MB) FROM(
select d.owner,d.table_name as object_name,sum(BYTES/1024/1024) MB from dba_extents a,dba_lobs d
where a.SEGMENT_NAME=d.SEGMENT_NAME and a.owner = D.owner and d.tablespace_name = 'ARM' and a.SEGMENT_TYPE like '%LOB%'
group by d.owner,d.table_name
union all
select owner,SEGMENT_NAME as object_name,sum(BYTES/1024/1024) MB from dba_extents
where tablespace_name = 'ARM' and SEGMENT_TYPE like '%TABLE%'
group by owner,SEGMENT_NAME) TMP
GROUP BY owner,object_name
ORDER BY SUM(MB) DESC;

2. 按行数

注意如果没收集过统计信息或者已经不准,会与实际有差异。

select TABLE_NAME,NUM_ROWS from dba_tables where OWNER='xxx' order by NUM_ROWS desc;

整理好后交给开发,确认各大表是否可清理,需保存多久数据。

二、 清理分类

目前大致遇到以下几种场景:

1. 可以drop

  • 备份表、临时表、已无用的表
  • 时间范围分区表:索引改为local索引后,按分区drop

2. 可以truncate

  • 部分日志表

注意对于大表(例如上百G的表)应该分次执行drop或者truncate,推荐方法参考:海量数据表删除方案_ITPUB博客

3. 可以rename然后重建空表

  • 可以暂停写入,不通过程序读取的表
  • 按业务要求看是否将最近几个月数据插回新表,插回后删除备份表
  • 或者不插回数据,几个月后删除备份表

4. 只能delete

  • 这个属于绝大部分情况,后面单独讨论

5. 业务接口删除

  • 业务关联性很强的表,不能简单按时间删除,需要由业务方编写删除程序或者使用标准接口,典型的案例就是ERP里的标准表。

三、 千万级以上大表如何delete

1. 直接删除的问题

  • 耗时长,可能最终遇到ORA-1555报错
  • 产生大事务,从库可能出现高延迟,且中断回滚耗时极长
  • 可能阻塞业务其他DML操作
  • undo表空间过度使用,可能影响到其他用户正常操作

下面虽然用到不同的删除语句,整体的思路都是一样的:分批删除并提交,将大事务化为小事务。另外,删除时注意归档及闪回日志产生量。

2. 按天删除数据

首先需要在对应时间字段(由业务方提供)加索引,一天的数据量大概在一两百万的话问题不大,再多可能就得拆得更细些。

  • 时间字段为时间类型(date,timestamp)
DECLAREbegin_date date := to_date('2020-03-01','yyyy-mm-dd');
BEGINWHILE begin_date < to_date('2020-05-01','yyyy-mm-dd') loopDELETE FROM MYTAB WHERE CREATE_DT >= begin_date and CREATE_DT < begin_date+1;commit;begin_date := begin_date+1;end loop;
END;
/
  • 时间字段为字符串类型(奇葩设计,但就是有)
DECLAREbegin_date date := to_date('20180901','yyyymmdd');
BEGIN   WHILE begin_date < to_date('20181001','yyyymmdd') loopDELETE FROM APS.KIT_MATERIAL_RESULT_ALL WHERE KIT_DAY = to_char(begin_date,'yyyymmdd');commit;begin_date := begin_date+1;end loop;
END;
/

3. 游标对比rowid批量删除

下面两个方法来自:Oracle库Delete删除千万以上普通堆表数据的方法 - AlfredZhao - 博客园

有部分调整,避免全表取数及循环判断时间,原代码请参考原文

删除2020年3-4月的数据,每1万行提交一次

-- del_cur 游标名
-- tmp 要删除数据的表名
-- create_dt 时间字段名declarecursor del_cur is select a.rowid row_id from tmp where create_dt >= to_date('2020-03-01','yyyy-mm-dd') and create_dt < to_date('2020-05-01','yyyy-mm-dd') order by a.rowid;
beginfor v_cusor in del_cur loopdelete from tmp where rowid = v_cusor.row_id;if mod(del_cur%rowcount,10000)=0 thencommit;end if;end loop;commit;
end;
/

3. 直接按rowid删除

-- del_cur 游标名
-- tmp 要删除数据的表名declare
maxrows number default 10000;
delete_cnt number default 0;
begin
select count(1)/maxrows into delete_cnt from tmp where create_dt >= to_date('2020-03-01','yyyy-mm-dd') and create_dt < to_date('2020-05-01','yyyy-mm-dd');
for i in 1..TRUNC(delete_cnt)+1
loop
delete tmp where create_dt >= to_date('2020-03-01','yyyy-mm-dd') and create_dt < to_date('2020-05-01','yyyy-mm-dd') and rownum <= maxrows;
commit;
end loop;
end;
/

4. 如何看delete释放了多少空间

由于delete不降低高水位线,不能直接看表的大小,但也可以通过空闲块的计算来看。TOM大师脚本-show space 多个版本,谢谢大牛们_dnil27295的博客-CSDN博客

下面代码基于 exec show_space_1810 改了一下,原代码出现了两次Total Blocks和Total bytes,调整了名字,同时简化了输出的内容。输出如下,可以看到释放空间约228G。

-- 使用方法
-- set serveroutput on
-- exec show_space('TABLE_NAME','OWNER');
-- 各字段含义参考:https://docs.oracle.com/database/121/ARPLS/d_space.htm#ARPLS68113create or replace procedure show_space
( p_segname_1 in varchar2,
p_owner_1 in varchar2 default user,
p_type_1 in varchar2 default 'TABLE',
p_space in varchar2 default 'AUTO',
p_analyzed in varchar2 default 'Y'
)
as
p_segname varchar2(100);
p_type varchar2(10);
p_owner varchar2(30);
l_unformatted_blocks number;
l_unformatted_bytes number;
l_fs1_blocks number;
l_fs1_bytes number;
l_fs2_blocks number;
l_fs2_bytes number;
l_fs3_blocks number;
l_fs3_bytes number;
l_fs4_blocks number;
l_fs4_bytes number;
l_full_blocks number;
l_full_bytes number;
l_free_blks number;
l_total_blocks number;
l_total_bytes number;
l_unused_blocks number;
l_unused_bytes number;
l_LastUsedExtFileId number;
l_LastUsedExtBlockId number;
l_LAST_USED_BLOCK number;procedure p( p_label in varchar2, p_num in number )
is
begin
dbms_output.put_line( rpad(p_label,40,'.') || p_num );
end;begin
p_segname := upper(p_segname_1); -- rainy changed
p_owner := upper(p_owner_1);
p_type := p_type_1;if (p_type_1 = 'i' or p_type_1 = 'I') then --rainy changed
p_type := 'INDEX';
end if;if (p_type_1 = 't' or p_type_1 = 'T') then --rainy changed
p_type := 'TABLE';
end if;if (p_type_1 = 'c' or p_type_1 = 'C') then --rainy changed
p_type := 'CLUSTER';
end if;dbms_space.unused_space
( segment_owner => p_owner,
segment_name => p_segname,
segment_type => p_type,
total_blocks => l_total_blocks,
total_bytes => l_total_bytes,
unused_blocks => l_unused_blocks,
unused_bytes => l_unused_bytes,
LAST_USED_EXTENT_FILE_ID => l_LastUsedExtFileId,
LAST_USED_EXTENT_BLOCK_ID => l_LastUsedExtBlockId,
LAST_USED_BLOCK => l_LAST_USED_BLOCK
);if p_space = 'MANUAL' or (p_space <> 'auto' and p_space <> 'AUTO') then
dbms_space.free_blocks
( segment_owner => p_owner,
segment_name => p_segname,
segment_type => p_type,
freelist_group_id => 0,
free_blks => l_free_blks );
p( 'Free Blocks', l_free_blks );
end if;/*IF the segment is analyzed */
if p_analyzed = 'Y' then
dbms_space.space_usage(segment_owner => p_owner ,
segment_name => p_segname ,
segment_type => p_type ,
unformatted_blocks => l_unformatted_blocks ,
unformatted_bytes => l_unformatted_bytes,
fs1_blocks => l_fs1_blocks,
fs1_bytes => l_fs1_bytes ,
fs2_blocks => l_fs2_blocks,
fs2_bytes => l_fs2_bytes,
fs3_blocks => l_fs3_blocks ,
fs3_bytes => l_fs3_bytes,
fs4_blocks => l_fs4_blocks,
fs4_bytes => l_fs4_bytes,
full_blocks => l_full_blocks,
full_bytes => l_full_bytes);
-- dbms_output.put_line(rpad(' ',50,'*'));
-- dbms_output.put_line('The segment is analyzed');
-- p( 'Unformatted Blocks', l_unformatted_blocks );
-- p( 'Unformatted Bytes', l_unformatted_bytes );
p( 'Unused Blocks', l_unused_blocks );
p( 'Unused Bytes', l_unused_bytes );
p( '0% -- 25% free space blocks', l_fs1_blocks);
p( '0% -- 25% free space bytes', l_fs1_bytes);
p( '25% -- 50% free space blocks', l_fs2_blocks);
p( '25% -- 50% free space bytes', l_fs2_bytes);
p( '50% -- 75% free space blocks', l_fs3_blocks);
p( '50% -- 75% free space bytes', l_fs3_bytes);
p( '75% -- 100% free space blocks', l_fs4_blocks);
p( '75% -- 100% free space bytes', l_fs4_bytes);
p( 'Full Blocks', l_full_blocks);
p( 'Full bytes', l_full_bytes);
p( 'Total Blocks', l_total_blocks );
p( 'Total Bytes', l_total_bytes );
-- p( 'Last Used Ext FileId', l_LastUsedExtFileId );
-- p( 'Last Used Ext BlockId', l_LastUsedExtBlockId );
-- p( 'Last Used Block', l_LAST_USED_BLOCK );
end if;
end show_space;
/

四、 分区化改造

对于需要定期清理的表,建议在线重定义为分区表,提高删除效率。这又是一个很长的话题了:Oracle 利用在线重定义进行分区表转换_Hehuyi_In的博客-CSDN博客_oracle分区表转换

参考

海量数据表删除方案_ITPUB博客

Oracle库Delete删除千万以上普通堆表数据的方法 - AlfredZhao - 博客园

TOM大师脚本-show space 多个版本,谢谢大牛们_dnil27295的博客-CSDN博客

Oracle 利用在线重定义进行分区表转换_Hehuyi_In的博客-CSDN博客_oracle分区表转换

SQL Server 快速删除/归档数据方法小结_Hehuyi_In的博客-CSDN博客_sqlserver 快速删除数据

Oracle 大表数据删除/清理方法小结相关推荐

  1. oracle 大表删除数据后,回收空间的问题。

    在oracle中由于表结构设计不合理或者需要清楚老数据的时候,经常需要对大表数据进行清理. 一般有一下几种方法: 1. 删除大部分数据,留下小部分数据.我们可以把需要保留的数据转移到别的表,然后再把大 ...

  2. Oracle数据库重复数据删除的三种情况

    在对数据库进行操作过程中我们可能会遇到这种情况,表中的数据可能重复出现,使我们对数据库的操作过程中带来很多的不便,那么怎么删除这些重复没有用的数据呢? 重复数据删除技术可以提供更大的备份容量,实现更长 ...

  3. ORACLE 大表使用 rowid 切片备份到历史表

    ORACLE 大表使用 rowid 切片备份到历史表 概述与要求 思路 备份与清理表的信息 方案 操作前准备 切分字段 备份步骤 清理步骤 脚本 概述与要求 项目需求将几个5000万行大表先备份到历史 ...

  4. oracle创建索引表,Oracle 大表创建索引

    Oracle 大表创建索引 祖仙教小凡仙 海鲨数据库架构师 有个2亿记录的表,发现需要添加一个联合索引,结果就采用普通的create index index_name on tablename (en ...

  5. MySQL-在线处理大表数据 在线修改大表的表结构

    文章目录 生猛干货 官方文档 概述 示例 大表数据的分批处理 修改大表的表结构 方案一 : 从表修改,主从切换 方案二: pt-online-schema-change 搞定MySQL 生猛干货 带你 ...

  6. oracle 建表字段设置,Oracle创建表、删除表、修改表(添加字段、修改字段、删除字段)语句总结...

    关于Oracle创建表.删除表.修改表(添加字段.修改字段.删除字段)语句的简短总结. Oracle创建表: create table 表名 ( 字段名1 字段类型 默认值 是否为空 , 字段名2 字 ...

  7. Oracle大表清理truncate .. reuse storage

    Oracle大表清理truncate .. reuse storage deallocate_unused_clause Purpose Use the deallocate_unused_claus ...

  8. Mysql大表数据清理

    假设大表表名:t_log delete操作会记录mysql日志,大数据量的清理会耗费大量时间,所以一般得用其他方式. 1.如果大表数据对我们来说不重要,完全可以直接清理,那么可以用truncate t ...

  9. MySQL快速清空大表数据(truncate table table_name;)

    MySQL快速清空大表数据 项目初次上线,进行性能测试造的数据量巨大,都是些不可用数据,但又有一些是必须保留的,很多时候需要进行系统性的清理数据或者是,将有用的数据筛选出来之后再插入到表中!保留表结构 ...

  10. Pl/sql 如何将oracle的表数据导出成excel文件?

    oracle将表数据导出成excel文件的方法 1)在SQL窗体上,查询需要导出的数据 --查询数据条件--select MID,CODE,NAME from Dxc_Goods_Cate where ...

最新文章

  1. 初学者学python好还是c-C 和 Python语言先学哪个好?
  2. 文巾解题 面试题 01.06. 字符串压缩
  3. 编程实现将rdd转换为dataframe:源文件内容如下(_大数据 什么是RDD?可以干什么?为什么要有RDD?...
  4. LeetCode 142——环形链表 II
  5. 计算机网络技术发源于什么,计算机网络基础试题和答案
  6. linux 433发送驱动
  7. 96秒100亿!如何抗住双11高并发流量?
  8. 学校为什么要单位接收函_学校为什么要做校园文化建设?
  9. 三星 9810 android 9,【极光ROM】-【三星NOTE9 N960X-9810】-【V17.0 Android-Q-TB9】
  10. Nginx学习笔记(Docker版)-2
  11. Python中常用的四个小技巧总结
  12. 办理登机的英语词组手机键盘_如何使用智能手机使登机轻而易举
  13. python tkinter编写界面,使用win32com操作excel获取数据生成截图后,wxpy登录微信,给租客发送房租(二)
  14. Nirvana Chain 为应用而生技术交流酒会在成都成功举办
  15. 【c语言】一个球从100米高的自由落下,每次落地后反跳回原高度的一半,再落下,再反弹。求第 10次落地时,共经过多少米,第10次反弹多高。
  16. 简单快速的“0x800c0006 安装失败 .NET framework 等旧版本软件安装失败”的解决方法
  17. 宇宙即计算~一种新科学:斯蒂芬·沃尔夫勒姆
  18. Java微信公众号配置验证Token
  19. 我发现了 tinder 的留存密码
  20. Blog地址更新:http://www.javabar.com.cn

热门文章

  1. android dfu升级
  2. Eclipse连接Github出现not authorized
  3. Mint-ui MessageBox.confirm 确定和取消事件
  4. 如何让房间每一个角落都充满 Wi-Fi?
  5. 树莓派正式开售CM4以及CM4 Lite,32个不同配置,最低25美元起售!
  6. java: Compilation failed: internal java compiler error
  7. 神经网络压缩 剪枝 量化 嵌入式计算优化NCNN mobilenet squeezenet shufflenet
  8. edge 临时文件夹 位置_如何更改Microsoft Edge的下载文件夹的位置
  9. 个性化定制软件安装包流程指导(按照步骤即可定制化安装流程)
  10. 笔记本处理器排名_上半年最受欢迎处理器TOP10榜单:AMD终进榜,9代酷睿无缘前10...