解决方案
move tablespace概要说明:
move tablespace 操作可以收缩段、消除部分行迁移、消除空间碎片、使数据更紧密。move tablespace 操作会降低高水位线,但不会释放申请的空间。
可以使用move将一个表从当前的tablespace中移动到另一个tablespace中,或者仅在当前的tablespace中移动。
move操作时,会在表上添加exclusive lock,此时不允许其它DML操作,仅可以查询。
move操作时,需要目标tablespace中有1倍于表大小的空闲空间以供使用。
move操作后,数据的rowid发生了改变,所以表上的index是必须要rebuild的。
本案例中数据库版本为11.2.0.4,只在表的当前表空间中进行move 操作,本次不涉及lob字段。
一、普通表
1、查看使用指定表空间的用户及数据量
select owner,sum(bytes/1024/1024/1024) gb from dba_segments
where tablespace_name='DATA_TBS'
group by owner
order by gb desc;
2、创建表名及表实际大小信息相关表
创建存放表名信息的表
create table system.TEMP_EDW_SEGMENTS
(
SEGMENT_NAME VARCHAR2(40),
OWNER VARCHAR2(20)
);
创建数据大小信息表,REAL_SIZE_MB 表示实际数据大小,SEG_SIZE_MB表示数据段大小
SEG_SIZE_MB 减去 REAL_SIZE_MB ,近似于碎片大小,单位 MB
create table system.FRAG_RESAULTS
(
OWNER VARCHAR2(20),
TNAME VARCHAR2(40),
REAL_SIZE_MB NUMBER,
SEG_SIZE_MB NUMBER
);
3、向表名信息表中插入数据
本次统计超过1GB 的表
insert into system.temp_edw_segments
select segment_name,owner from dba_segments
where segment_type='TABLE'
and owner='EDW'
and TABLESPACE_NAME='DATA_TBS'
and (bytes/1024/1024/1024)>1;
commit;
4、创建计算表或分区实际大小的函数
该函数可以指定四个参数,分别是数据段名称、用户名、数据段类型、分区名
其返回结果为表或分区实际的大小,单位为bytes
CREATE OR REPLACE FUNCTION REAL_SIZE(
P_SEGNAME IN VARCHAR2,
P_OWNER IN VARCHAR2 DEFAULT USER,
P_TYPE IN VARCHAR2 DEFAULT 'TABLE',
P_PART_NAME IN VARCHAR2 DEFAULT NULL)
RETURN NUMBER AUTHID CURRENT_USER AS
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;
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;
T_TOTAL_BYTES NUMBER;
T_FS_BYTES NUMBER;
BEGIN
DBMS_SPACE.SPACE_USAGE(
P_OWNER,
P_SEGNAME,
P_TYPE,
L_UNFORMATTED_BLOCKS,
L_UNFORMATTED_BYTES,
L_FS1_BLOCKS,
L_FS1_BYTES,
L_FS2_BLOCKS,
L_FS2_BYTES,
L_FS3_BLOCKS,
L_FS3_BYTES,
L_FS4_BLOCKS,
L_FS4_BYTES,
L_FULL_BLOCKS,
L_FULL_BYTES,
P_PART_NAME);
DBMS_SPACE.UNUSED_SPACE(
P_OWNER,
P_SEGNAME,
P_TYPE,
L_TOTAL_BLOCKS,
L_TOTAL_BYTES,
L_UNUSED_BLOCKS,
L_UNUSED_BYTES,
L_LASTUSEDEXTFILEID,
L_LASTUSEDEXTBLOCKID,
L_LAST_USED_BLOCK,
P_PART_NAME);
T_FS_BYTES := L_FS1_BYTES * 0.25 / 2 + L_FS2_BYTES * (0.5 + 0.25) / 2 +L_FS3_BYTES * (0.75 + 0.5) / 2 +L_FS4_BYTES * (1 + 0.75) / 2 + L_UNUSED_BYTES;
T_TOTAL_BYTES := L_TOTAL_BYTES;
RETURN T_TOTAL_BYTES-T_FS_BYTES;
EXCEPTION
WHEN OTHERS THEN
RETURN 1;
END;
/
5、查询表碎片信息
数据段大小减去实际数据大小,近似于碎片大小
--查询指定表数据段大小
select bytes/1024/1024 seg_size_mb from dba_segments where owner='EDW' and segment_name='EMPLOYEES';
--查询指定表实际数据大小
select trunc(real_size('EMPLOYEES','EDW')/1024/1024, 2) real_size_mb from dual;
编写查询脚本,依次查询system.temp_edw_segments中所有表的实际数据大小信息,并将结果写入到system.FRAG_RESAULTS 表
spool suipian.log;
set serveroutput on;
declare
cursor c1 is select * from system.temp_edw_segments;
v_sql long;
begin
for i in c1 loop
v_sql := 'insert into system.frag_resaults
select owner,segment_name,
trunc(real_size('''||i.segment_name||''','''||i.owner||''')/1024/1024, 2) real_size_mb,
bytes/1024/1024 seg_size_mb
from dba_segments s where s.owner='''||i.owner||''' and s.segment_name='''||i.segment_name||'''';
execute immediate v_sql;
dbms_output.put_line(v_sql);
commit;
end loop;
end;
/
spool off;
exit;
导出system.FRAG_RESAULTS 表数据,对其中碎片较大的表进行move tablespace 操作,用以收缩表碎片。
6、生成移动表及重建索引的语句
生成移动表的语句
select 'ALTER TABLE '||owner||'.'||table_name||' move parallel 8 nologging;' sqltext
from dba_tables where table_name in
(
'DEPARTMENTS',
'REGIONS',
'LOCATIONS',
'JOBS',
'EMPLOYEES',
'JOB_HISTORY'
) and owner='EDW';
生成rebuild 索引的语句
set lines 300;
set pages 300;
select 'ALTER INDEX '||owner||'.'||index_name||' REBUILD online parallel 8 nologging;' sqltext
from dba_indexes
where table_name in(
'DEPARTMENTS',
'REGIONS',
'LOCATIONS',
'JOBS',
'EMPLOYEES',
'JOB_HISTORY'
) and owner='EDW';
执行上述拼接出来的语句,或者写入脚本统一执行。
7、检查表及索引状态
检查表的状态
select object_name,object_type,status from dba_objects where object_name
in(
'DEPARTMENTS',
'REGIONS',
'LOCATIONS',
'JOBS',
'EMPLOYEES',
'JOB_HISTORY'
) AND OWNER='EDW';
检查索引的状态
select table_name,index_name,status from dba_indexes
where table_name in
(
'DEPARTMENTS',
'REGIONS',
'LOCATIONS',
'JOBS',
'EMPLOYEES',
'JOB_HISTORY'
) AND OWNER='EDW'
and status<>'VALID';
8、修改表和索引的并行
表的并行
select 'alter table '||owner||'.'||table_name||' parallel 1;' sqltext
from dba_tables where table_name in
(
'DEPARTMENTS',
'REGIONS',
'LOCATIONS',
'JOBS',
'EMPLOYEES',
'JOB_HISTORY'
) and owner='EDW';
索引的并行
select 'ALTER INDEX '||owner||'.'||index_name||' noparallel;' sqltext
from dba_indexes where table_name in
(
'DEPARTMENTS',
'REGIONS',
'LOCATIONS',
'JOBS',
'EMPLOYEES',
'JOB_HISTORY'
) and owner='EDW';
执行上述拼接出来的语句,或者写入脚本统一执行。
9、收集统计信息
exec dbms_stats.gather_table_stats(ownname => 'EDW',tabname => 'EMPLOYEES',estimate_percent => 10,method_opt=> 'FOR ALL COLUMNS SIZE AUTO',degree => '8',cascade=>TRUE);
二、分区表
1、查看使用指定表空间的用户及数据量
select owner,sum(bytes/1024/1024/1024) gb from dba_segments
where tablespace_name='DATA_TBS_HIS'
group by owner
order by gb desc;
2、创建表名及表实际大小信息相关表
创建存放表名信息的表
create table system.TEMP_PART_EDW_SEGMENTS
(
SEGMENT_NAME VARCHAR2(40),
OWNER VARCHAR2(20),
PARTITION_NAME VARCHAR2(30)
);
创建数据大小信息表,REAL_SIZE_MB 表示实际数据大小,SEG_SIZE_MB表示数据段大小
PNAME表示分区名,SEG_SIZE_MB 减去 REAL_SIZE_MB ,近似于碎片大小,单位 MB
create table system.FRAG_PART_RESAULTS
(
OWNER VARCHAR2(20),
TNAME VARCHAR2(40),
PNAME VARCHAR2(30),
REAL_SIZE_MB NUMBER,
SEG_SIZE_MB NUMBER
);
3、向表名信息表中插入数据
本次统计超过1GB 的分区
insert into system.temp_part_edw_segments
select segment_name,owner,PARTITION_NAME from dba_segments
where segment_type='TABLE PARTITION'
and owner='EDW'
and TABLESPACE_NAME='DATA_TBS_HIS'
and (bytes/1024/1024/1024)>1;
commit;
4、查询表碎片信息
数据段大小减去实际数据大小,近似于碎片大小
--查询指定分区数据段大小
select OWNER,SEGMENT_NAME,PARTITION_NAME,SEGMENT_TYPE,TABLESPACE_NAME,bytes/1024/1024 mb from dba_segments
where segment_type='TABLE PARTITION'
and owner='EDW'
and TABLESPACE_NAME='DATA_TBS_HIS'
and segment_name='TABLE_HIS'
and PARTITION_NAME='P_202312';
--查询指定分区实际数据大小
select trunc(real_size('TABLE_HIS','EDW','TABLE PARTITION','P_202312')/1024/1024, 2) real_size_mb from dual;
编写查询脚本,依次查询system.temp_part_edw_segments中所有表的实际数据大小信息,并将结果写入到system.FRAG_PART_RESAULTS 表
spool part_suipian.log;
set serveroutput on;
declare
cursor c1 is select * from system.temp_part_edw_segments;
v_sql long;
begin
for i in c1 loop
v_sql := 'insert into system.frag_part_resaults
select owner,segment_name,partition_name,
trunc(real_size('''||i.segment_name||''','''||i.owner||''',''TABLE PARTITION'','''||i.partition_name||''')/1024/1024, 2) real_size_mb,
bytes/1024/1024 seg_size_mb
from dba_segments s where s.owner='''||i.owner||''' and s.segment_name='''||i.segment_name||''' and s.partition_name='''||i.partition_name||'''';
execute immediate v_sql;
dbms_output.put_line(v_sql);
commit;
end loop;
end;
/
spool off;
exit;
导出system.FRAG_PART_RESAULTS 表数据,对其中碎片较大的表进行move tablespace 操作,用以收缩表碎片。
5、生成移动分区及重建分区索引的语句
生成移动分区的语句
select 'ALTER TABLE '||table_owner ||'.'||table_name||' MOVE PARTITION '||partition_name||' TABLESPACE DATA_TBS_HIS;' sqltext
from dba_tab_partitions
where table_owner='EDW'
and table_name='TABLE_HIS'
and PARTITION_NAME='P_202312';
执行拼接出来的语句,然后再进行后续操作
生成rebuild 分区索引的语句
select 'ALTER INDEX '||index_owner ||'.'||index_name||' rebuild partition '||partition_name||' TABLESPACE DATA_TBS_HIS;' sqltext
from dba_ind_partitions where status='UNUSABLE';
6、检查表及索引状态
检查表的状态
select object_name,object_type,status from dba_objects where object_name
in(
'TABLE_HIS'
) and owner='EDW';
检查索引的状态
select index_owner,index_name,partition_name,status
from dba_ind_partitions a where index_name in
(select index_name from dba_part_indexes where table_name='TABLE_HIS')
and index_owner='EDW'
and status<>'USABLE';