Oracle move和shrink释放高水位空间 (一)

move 和shrink 的共同点

1、收缩段

2、消除部分行迁移

3、消除空间碎片

4、使数据更紧密

一、shrink

语法:

alter table TABLE_NAME shrink space [compact|cascate]

segment shrink执行的两个阶段:

1、数据重组(compact):

通过一系列insert、delete操作,将数据尽量排列在段的前面。在这个过程中需要在表上加RX锁,即只在需要移动的行上加锁。

由于涉及到rowid的改变,需要enable row movement.同时要disable基于rowid的trigger.这一过程对业务影响比较小。

2、HWM调整:第二阶段是调整HWM位置,释放空闲数据块。

此过程需要在表上加X锁,会造成表上的所有DML语句阻塞。在业务特别繁忙的系统上可能造成比较大的影响。

注意:shrink space语句两个阶段都执行。

shrink space compact只执行第一个阶段。

如果系统业务比较繁忙,可以先执行shrink space compact重组数据,然后在业务不忙的时候再执行shrink space降低HWM释放空闲数据块。

举例

  alter table TABLE_NAME shrink space compact;  只整理碎片 不回收空间,  
  alter table TABLE_NAME shrink space;                 整理碎片并回收空间。
  alter table TABLE_NAME shrink space cascade;    整理碎片回收空间 并连同表的级联对象一起整理(比如索引)
  alter table pt_table modify  PARTITION P1 shrink space cascade;  分区表

shrink的优点

1.可在线执行

2.可使用参数cascade,同时收缩表上的索引

3.执行后不会导致索引失效

4.可避免alter table move执行过程中占用很多表空间(如果表10G大小,那alter table move差不多还得需要10G空间才能执行)。

二、move

1、move table的功能:

①:将一个table从当前的tablespace上移动到另一个tablespace上:

②:来改变table已有的block的存储参数,如:alter table t move storage (initial 30k next 50k);

③:move操作也可以用来解决table中的行迁移的问题。

2、使用move的一些注意事项:

①:table上的index需要rebuild:

在前面我们讨论过,move操作后,数据的rowid发生了改变,我们知道,index是通过rowid来fetch数据行的,所以,table上的index是必须要rebuild的。

alter index index_name rebuild online;

②:move时对table的锁定

当我们对table进行move操作时,查询v$locked_objects视图可以发现,table上加了exclusive lock

③:关于move时空间使用的问题:

当我们使用alter table move来降低table的HWM时,有一点是需要注意的,这时,当前的tablespace中需要有1倍于table的空闲空间以供使用。

三、move和hrink的区别是:

1、move后,表在表空间中的位置肯定会变,可能前移也可能后移,一般来说如果该表前面的表空间中有足够空间容纳该表,则前移,否则后移。

2、hrink后,表在表空间中的位置肯定不变,也就是表的段头位置不会发生变化。

3、Move会移动高水位,但不会释放申请的空间,是在高水位以下(below HWM)的操作。

4、shrink space 同样会移动高水位,但也会释放申请的空间,是在高水位上下(below and above HWM)都有的操作。

5、使用move时,会改变一些记录的ROWID,所以MOVE之后索引会变为无效,需要REBUILD。

6、使用shrink space时,索引会自动维护。如果在业务繁忙时做压缩,

可以先shrink space compact,来压缩数据而不移动HWM,等到不繁忙的时候再shrink space来移动HWM。

7、shrink可以单独压缩索引,alter index xxx shrink space来压缩索引。另外、压缩表时指定Shrink space cascade会同时压缩索引,

四、实战实验:

实验环境:Oracle11.2.0.4

[[email protected] ~]$ sqlplus ‘/as sysdba‘
SQL*Plus: Release 11.2.0.4.0 Production on Thu Aug 10 14:44:59 2017
Copyright (c) 1982, 2013, Oracle.  All rights reserved.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL>

1、创建两张测试表:test_1 和 test_2

SQL> create table test_1 (name varchar2(10)) storage (initial 500m next 1m);
Table created.
SQL> create table test_2 (name varchar2(10)) storage (initial 500m next 1m);
SQL>  create index idx_test1 on test_1(name);
Index created.
SQL>  create index idx_test2 on test_2(name);
Index created.

2、插入数据,并收集统计信息:

SQL> insert into test_1 values(‘zhang‘);
SQL> insert into test_1 values(‘zhang‘);
SQL> insert into test_2 values(‘zhang‘);
SQL> insert into test_2 values(‘zhang‘);
SQL> exec dbms_stats.gather_table_stats(ownname =>‘ADMIN‘,tabname =>‘TEST_1‘,cascade=>TRUE);
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats(ownname =>‘ADMIN‘,tabname =>‘TEST_2‘,cascade=>TRUE);
PL/SQL procedure successfully completed.
SQL>

3、查看两张表的blocks信息:

SQL> select B.SEGMENT_NAME, B.blocks,B.blocks * 8096 / 1024 / 1024, A.BLOCKS,A.blocks * 8096 / 1024 / 1024, A.EMPTY_BLOCKS from user_tables a, 
    USER_SEGMENTS B WHERE TABLE_NAME in (‘TEST_1‘,‘TEST_2‘) AND A.TABLE_NAME = B.SEGMENT_NAME; 
SEGMENT_NAME                                                                          BLOCKS B.BLOCKS*8096/1024/1024     BLOCKS A.BLOCKS*8096/1024/1024 EMPTY_BLOCKS
--------------------------------------------------------------------------------- ---------- ----------------------- ---------- ----------------------- ------------
TEST_1                                                                                 64512               498.09375        222              1.71405029            0
TEST_2                                                                                 64512               498.09375        222              1.71405029            0
SQL> select TABLE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name in (‘TEST_1‘,‘TEST_2‘);
TABLE_NAME                         BLOCKS EMPTY_BLOCKS
------------------------------ ---------- ------------
TEST_1                                222            0
TEST_2                                222            0
SQL> select owner,segment_name,sum(bytes)/1024/1024 MB from dba_segments where tablespace_name=‘TEST‘ and segment_type like ‘%TAB%‘ group by owner,segment_name order by MB desc;
OWNER                          SEGMENT_NAME                                                                              MB
------------------------------ --------------------------------------------------------------------------------- ----------
ADMIN                          TEST_2                                                                                   504
ADMIN                          TEST_1                                                                                   504
SQL> select index_name,table_name,status from user_indexes where table_name in (‘TEST_1‘,‘TEST_2‘);    ---索引状态都正常
INDEX_NAME                     TABLE_NAME                     STATUS
------------------------------ ------------------------------ --------
IDX_TEST2                      TEST_2                         VALID
IDX_TEST1                      TEST_1                         VALID
SQL>

----从上面可以看出,由于我们预分配给了两张表500M,那么他们俩现在一共有64512个blocks,共有500M,而实际只占用了222个,

4、删除两张表的数据,并收集统计信息然后查看两张表的blocks信息:

SQL> delete from test_1 where rownum <=1;
1 row deleted.
SQL> delete from test_2 where rownum <=1;
1 row deleted.
SQL> exec dbms_stats.gather_table_stats(ownname =>‘ADMIN‘,tabname =>‘TEST_1‘,cascade=>TRUE);
PL/SQL procedure successfully completed.
SQL>  exec dbms_stats.gather_table_stats(ownname =>‘ADMIN‘,tabname =>‘TEST_2‘,cascade=>TRUE);
PL/SQL procedure successfully completed.
SQL> select TABLE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name in (‘TEST_1‘,‘TEST_2‘);
TABLE_NAME                         BLOCKS EMPTY_BLOCKS
------------------------------ ---------- ------------
TEST_1                                222            0
TEST_2                                222            0
SQL> select B.SEGMENT_NAME, B.blocks,B.blocks * 8096 / 1024 / 1024, A.BLOCKS,A.blocks * 8096 / 1024 / 1024, A.EMPTY_BLOCKS from user_tables a, 
  2      USER_SEGMENTS B WHERE TABLE_NAME in (‘TEST_1‘,‘TEST_2‘) AND A.TABLE_NAME = B.SEGMENT_NAME; 
SEGMENT_NAME                                                                          BLOCKS B.BLOCKS*8096/1024/1024     BLOCKS A.BLOCKS*8096/1024/1024 EMPTY_BLOCKS
--------------------------------------------------------------------------------- ---------- ----------------------- ---------- ----------------------- ------------
TEST_1                                                                                 64512               498.09375        222              1.71405029            0
TEST_2                                                                                 64512               498.09375        222              1.71405029            0
SQL> 
SQL> select index_name,table_name,status from user_indexes where table_name in (‘TEST_1‘,‘TEST_2‘);    ---此时索引状态都正常
INDEX_NAME                     TABLE_NAME                     STATUS
------------------------------ ------------------------------ --------
IDX_TEST2                      TEST_2                         VALID
IDX_TEST1                      TEST_1                         VALID

---从上面可以看出,虽然删除了表的数据,但是空间并没有释放,没有释放的空间包括高水位线以上和高水位线以下。(高水位线上面的空间就是预分配的空间 减去 实际占用的空间;

高水位线以下的空间就是数据实际占用的空间--因为delete是不会是否空间的,也就是说高水位一直存在除非新插入的数据将其覆盖)

5、对test_1表进行move操作:

SQL> alter table test_1 move;
Table altered.
SQL> exec dbms_stats.gather_table_stats(ownname =>‘ADMIN‘,tabname =>‘TEST_1‘,cascade=>TRUE);
PL/SQL procedure successfully completed.
SQL> select B.SEGMENT_NAME, B.blocks,B.blocks * 8096 / 1024 / 1024, A.BLOCKS,A.blocks * 8096 / 1024 / 1024, A.EMPTY_BLOCKS from user_tables a, 
  2         USER_SEGMENTS B WHERE TABLE_NAME in (‘TEST_1‘,‘TEST_2‘) AND A.TABLE_NAME = B.SEGMENT_NAME; 
SEGMENT_NAME                                                                          BLOCKS B.BLOCKS*8096/1024/1024     BLOCKS A.BLOCKS*8096/1024/1024 EMPTY_BLOCKS
--------------------------------------------------------------------------------- ---------- ----------------------- ---------- ----------------------- ------------
TEST_2                                                                                 64512               498.09375        222              1.71405029            0
TEST_1                                                                                 64384              497.105469         35              .270233154            0
SQL> select index_name,table_name,status from user_indexes where table_name in (‘TEST_1‘,‘TEST_2‘); 
INDEX_NAME                     TABLE_NAME                     STATUS
------------------------------ ------------------------------ --------
IDX_TEST2                      TEST_2                         VALID
IDX_TEST1                      TEST_1                         UNUSABLE

---从上面可以看出,对表做了move后,该表实际占用的空间已经释放了,但是预分配的空间始终没有变化,这说明move操作会释放高水位以下的空间,但是不会释放高水位以上的空间;同时 test_1表的索引已经失效了!

6、对test_2表做shrink space操作:

SQL> alter table test_2 enable row movement;
Table altered.
SQL> alter table test_2 shrink space;
Table altered.
SQL> exec dbms_stats.gather_table_stats(ownname =>‘ADMIN‘,tabname =>‘TEST_2‘,cascade=>TRUE);
PL/SQL procedure successfully completed.
SQL> select B.SEGMENT_NAME, B.blocks,B.blocks * 8096 / 1024 / 1024, A.BLOCKS,A.blocks * 8096 / 1024 / 1024, A.EMPTY_BLOCKS from user_tables a, 
  2            USER_SEGMENTS B WHERE TABLE_NAME in (‘TEST_1‘,‘TEST_2‘) AND A.TABLE_NAME = B.SEGMENT_NAME; 
SEGMENT_NAME                                                                          BLOCKS B.BLOCKS*8096/1024/1024     BLOCKS A.BLOCKS*8096/1024/1024 EMPTY_BLOCKS
--------------------------------------------------------------------------------- ---------- ----------------------- ---------- ----------------------- ------------
TEST_2                                                                                    40              .308837891          1              .007720947            0
TEST_1                                                                                 64384              497.105469         35              .270233154            0
SQL> 
SQL> select index_name,table_name,status from user_indexes where table_name=‘TEST_2‘;
INDEX_NAME                     TABLE_NAME                     STATUS
------------------------------ ------------------------------ --------
IDX_TEST2                      TEST_2                         VALID
SQL>

---从上面可以看出预分配的空间全部释放了,说明shrink space 同样会移动高水位,但也会释放申请的空间,是在高水位上下(below and above HWM)都有的操作,并且索引不会失效。

注意:

①:使用move时,会改变一些记录的ROWID,所以MOVE之后索引会变为无效,需要REBUILD。

②:使用shrink space时,索引会自动维护。如果在业务繁忙时做压缩,可以先shrink space compact,来压缩数据而不移动HWM,等到不繁忙的时候再shrink space来移动HWM。

③:索引也是可以压缩的,压缩表时指定Shrink space cascade会同时压缩索引,也可以alter index xxx shrink space来压缩索引。

④:shrink space需要在表空间是自动段空间管理的,所以system表空间上的表无法shrink space。

---补充,move 也可以做到真正的压缩分配空间,只要指定STORAGE参数即可。:

SQL> alter table test_1 move storage (initial 1m);

时间: 2024-10-14 13:54:50

Oracle move和shrink释放高水位空间 (一)的相关文章

oralce move和shrink释放高水位

转自:https://blog.51cto.com/fengfeng688/1955137 move和shrink的共同点: 收缩段,消除部分行迁移,消除空间碎片,使数据更紧密 shrink用法: 语法: alter table TABLE_NAME shrink space [compact|cascades] shrink分为以下两个阶段: 1.数据重组(compact) 通过一系列的insert,delete操作,将数据尽量排列在段的前面.在这个过程中,需要在表上面加RX锁,即只在需要移动

Oracle—deallocate unused释放高水位空间(二)

deallocate unused :仅适用于释放HWM高水位以上的空间,而无法释放高水位以下的空间:比如对表预分配的空间 使用说明和方法,官方文档有说明,如下: Use the deallocate_unused_clause to explicitly deallocate unused space at the end of a database object segment and make the space available for other segments in the ta

Oracle 高水位说明和释放表空间,加快表的查询速度

高水位的介绍 数据库运行了一段时间,经过一些列的删除.插入.更改操作有些表的高水位线就有可能和实际的表存储数据的情况相差特别多,为了提高检索该表的效率,建议对这些表进行收缩: 查找高水位线的表 查找表需要的存储空间:表以数据块的形式存储在数据文件中,表的存储结构是:行×行数,如果知道了总共有多少行,每行的平均长度,两者相乘,再除于90%的使用率,那么就可以知道实际需要存储的空间: 表的存储结构; 从统计信息得出平均每行的长度和总共的行数,从而知道存储的SIZE: 查找表实际存储的空间:数据实际存

Oracle 高水位(HWM: High Water Mark)

一. 准备知识:ORACLE的逻辑存储管理. ORACLE在逻辑存储上分4个粒度: 表空间, 段, 区 和 块. 1.1 块: 是粒度最小的存储单位,现在标准的块大小是8K,ORACLE每一次I/O操作也是按块来操作的,也就是说当ORACLE从数据文件读数据时,是读取多少个块,而不是多少行. 每一个Block里可以包含多个row. 1.2 区: 由一系列相邻的块而组成,这也是ORACLE空间分配的基本单位,举个例子来说,当我们创建一个表Dave时,首先ORACLE会分配一区的空间给这个表,随着不

Oracle 高水位问题

Oracle 对数据段的管理有一个高水位(HWM, High Water Mark)的概念.高水位是数据段中使用过和未使用过的数据块的分界线.高水位以下的数据块是曾使用过的,以上的是从未被使用或初始化过的. 当 Oracle 进行全表扫描(FTS, Full table scan)的操作时,它会读高水位下的所有数据块.如果高水位下还有很多空闲空间(碎片),读取这些空闲数据块会降低操作的性能. 当针对一个表的删除操作很多时,表会产生大量碎片.删除操作释放的空间不会被插入操作立即重用,甚至永远也不会

高水位回收

用两种方法可以进行表的高水位回收1.用shrink进行高水位回收(不需要重建索引,不需要额外空间)alter table bydvmi.tpo enable row movement;alter table bydvmi.tpo shrink space cascade;alter table bydvmi.tpo disable row movement;exec DBMS_STATS.GATHER_TABLE_STATS(ownname=>'BYDVMI',tabname=>'TPO',E

[Oracle]高水位标记(HWM)

(一)高水位标记(High Water Mark,HWM)的概念 所谓高水位标记,是指一个已经分配的段中,已经使用的空间与未使用的空间的分界线.在表的使用过程中,随着数据的不断增多(insert),HWM不断向数据段未使用部分方向移动,而在删除数据(delete)的过程中,HWM并不会向反方向移动,即使删除全部数据,HWM依然不会改变.但是如果使用了truncate命令,则表的HWM会被重置为0. 图1.segment (二)高水位标记的影响 全表扫描要读出直到HWM标记的所有的属于该表的数据块

Oracle 高水位(HWM)回收原理及操作方法

一.  高水位(HWM)及其产生原因 High Water Mark,HWM) 是Oracle(Segment)级别的概念.在仅有DML(比如delete,insert)操作时,高水位线只会增长,不会降低.具体来说,由于程序使用的delete语句不回收使用过的空间,数据虽然删除了,但是高水位线却没有降低,仍然处于之前的水位. 下图为一个Segment内高水位不断增长的示意图: 注:一个表在初次插入记录时,Oracle会为其分配Segment和block. 插入大量数据后,高水位线随之增长 当数据

MOVE降低高水位 HWM

--创建实验表空间SQL> create tablespace andy03 datafile '/home/oracle/app/oradata/orcl/andy03.dbf'  size 10M autoextend on next 30M; --创建实验数据SQL> create table andy03 tablespace andy03  as select * from dba_objects ; SQL> insert into   andy03   select * f