Shrink详解.docx
《Shrink详解.docx》由会员分享,可在线阅读,更多相关《Shrink详解.docx(12页珍藏版)》请在冰豆网上搜索。
![Shrink详解.docx](https://file1.bdocx.com/fileroot1/2023-1/24/ca8ddc50-737c-4a5e-a650-099482e8ad19/ca8ddc50-737c-4a5e-a650-099482e8ad191.gif)
Shrink详解
从10g开始,oracle开始提供Shrink的命令,假如我们的表空间中支持自动段空间管理 (ASSM),就可以使用这个特性缩小段,即降低HWM。
这里需要强调一点,10g的这个新特性,仅对ASSM表空间有效,否则会报 ORA-10635:
Invalidsegmentortablespacetype。
如果经常在表上执行DML操作,会造成数据库块中数据分布稀疏,浪费大量空间。
同时也会影响全表扫描的性能,因为全表扫描需要访问更多的数据块。
从oracle10g开始,表可以通过shrink来重组数据使数据分布更紧密,同时降低HWM释放空闲数据块。
segmentshrink分为两个阶段:
1、数据重组(compact)
通过一系列insert、delete操作,将数据尽量排列在段的前面。
在这个过程中需要在表上加RX锁,即只在需要移动的行上加锁。
由于涉及到rowid的改变,需要enablerowmovement.同时要disable基于rowid的trigger.这一过程对业务影响比较小。
2、HWM调整
第二阶段是调整HWM位置,释放空闲数据块。
此过程需要在表上加X锁,会造成表上的所有DML语句阻塞。
在业务特别繁忙的系统上可能造成比较大的影响。
shrinkspace语句两个阶段都执行。
shrinkspacecompact只执行第一个阶段。
如果系统业务比较繁忙,可以先执行shrinkspacecompact重组数据,然后在业务不忙的时候再执行shrinkspace降低HWM释放空闲数据块。
shrink必须开启行迁移功能。
altertabletable_nameenablerowmovement;
注意:
altertableXXXenablerowmovement语句会造成引用表XXX的对象(如存储过程、包、视图等)变为无效。
执行完成后,最好执行一下utlrp.sql来编译无效的对象。
语法:
altertableshrinkspace[|compact|cascade];
其中:
altertableshrinkspacecompcat;
收缩表,相当于把块中数据打结实了,但会保持 highwatermark;
altertableshrinkspace;
收缩表,降低 highwatermark;
altertableshrinkspacecascade;
收缩表,降低 highwatermark,并且相关索引也要收缩一下。
alterindexidxnameshrinkspace;
回缩索引
1:
普通表
Sql脚本,该脚本会生成相应的语句:
select'altertable'||table_name||'enablerowmovement;'||chr(10)||'altertable'||table_name||'shrinkspace;'||chr(10)fromuser_tables;
select'alterindex'||index_name||'shrinkspace;'||chr(10)fromuser_indexes;
2:
分区表的处理
进行shrinkspace时 发生ORA-10631错误.shrinkspace有一些限制.
在表上建有函数索引(包括全文索引)会失败。
Sql脚本,改脚本会生成相应的语句:
select'altertable'||table_name||'enablerowmovement;'||chr(10)||'altertable'||table_name||'shrinkspace;'||chr(10)fromuser_tables;
select'alterindex'||index_name||'shrinkspace;'||chr(10)fromuser_indexeswhereuniqueness='NONUNIQUE';
select'altertable'||segment_name||'modifysubpartition'||partition_name||'shrinkspace;'||chr(10)fromuser_segmentswheresegment_type='TABLESUBPARTITION';
详细测试:
我们用系统视图all_objects来在上个测试的tablespaceASSM上创建测试表my_objects
SELECT TABLESPACE_NAME,
BLOCK_SIZE,
EXTENT_MANAGEMENT,
ALLOCATION_TYPE,
SEGMENT_SPACE_MANAGEMENT
FROM dba_tablespaces
WHERE TABLESPACE_NAME = 'HECV';
CREATE TABLE my_objects
TABLESPACE hecv
AS
SELECT * FROM all_objects;
然后我们随机地从tableMY_OBJECTS中删除一部分数据:
SQL> SELECT COUNT (*) FROM my_objects;
SQL>deletefrommy_objectswhereobject_namelike'%C%';
SQL>deletefrommy_objectswhereobject_namelike'%U%';
SQL>deletefrommy_objectswhereobject_namelike'%A%';
现在我们使用show_space()来看看my_objects的数据存储状况:
SQL>exec show_space('my_objects','auto','T','Y');
这里,tablemy_objects的HWM下有1152个block,其中,freespace为25-50%的block有303个,freespace为50-75%的block有284个,freespace为75-100%的block有409个.Totalblocks11个。
这种情况下,我们需要对这个table的现有数据行进行重组。
要使用assm上的shink,首先我们需要使该表支持行移动,可以用这样的命令来完成:
altertablemy_objectsenablerowmovement;
现在,就可以来降低my_objects的HWM,回收空间了,使用命令:
altertablebookingsshrinkspace;
我们具体的看一下实验的结果:
SQL>exec show_space('my_objects','auto','T','Y');
在执行玩shrink命令后,我们可以看到,tablemy_objects的HWM现在降到了456的位置,而且HWM下的block的空间使用状况,Totalblocks 的block有430个,freespace 为25-50%Block只有1个。
Shrink 的实现机制:
我们接下来讨论一下shrink的实现机制,我们同样使用讨论move机制的那个实验来观察。
CREATE TABLE TEST_HWM (id INT, name CHAR (2000))
TABLESPACE hecv;
INSERT INTO TEST_HWM VALUES (1, 'aa');
INSERT INTO TEST_HWM VALUES (2, 'bb');
INSERT INTO TEST_HWM VALUES (2, 'cc');
INSERT INTO TEST_HWM VALUES (3, 'dd');
INSERT INTO TEST_HWM VALUES (4, 'ds');
INSERT INTO TEST_HWM VALUES (5, 'dss');
INSERT INTO TEST_HWM VALUES (6, 'dss');
INSERT INTO TEST_HWM VALUES (7, 'ess');
INSERT INTO TEST_HWM VALUES (8, 'es');
INSERT INTO TEST_HWM VALUES (9, 'es');
INSERT INTO TEST_HWM VALUES (10, 'es');
我们来看看这个table的rowid和block的ID和信息:
SQL>SELECT ROWID, id, name FROM TEST_HWM;
SQL>SELECT EXTENT_ID,
FILE_ID,
RELATIVE_FNO,
BLOCK_ID,
BLOCKS
FROM dba_extents
WHERE segment_name = 'TEST_HWM';
然后从tabletest_hwm中删除一些数据:
deletefromTEST_HWMwhereid=2;
deletefromTEST_HWMwhereid=4;
deletefromTEST_HWMwhereid=3;
deletefromTEST_HWMwhereid=7;
deletefromTEST_HWMwhereid=8;
观察tabletest_hwm的rowid和blockid的信息:
SQL>selectrowid,id,namefromTEST_HWM;
从以上的信息,我们可以看到,在tabletest_hwm中,剩下的数据是分布在AAA10z,AAA100,AAA102,AAA103这样四个连续的block中。
SQL> exec show_space('TEST_HWM','auto','T','Y');
我们可以看到目前这四个block的空间使用状况,AAA10z,AAA100,AAA103上各有一行数据,我们猜测freespace为50-75%的3个block是这三个block,那么freespace为25-50%的1个block就是AAA102了,剩下freespace为 75-100% 的1个block,是HWM下已格式化的尚未使用的block。
然后,我们对tablemy_objects执行shtink的操作:
SQL>altertabletest_hwmenablerowmovement;
SQL>altertabletest_hwmshrinkspace;
SQL>selectrowid,id,namefromTEST_HWM;
SQL>SELECT EXTENT_ID,
FILE_ID,
RELATIVE_FNO,
BLOCK_ID,
BLOCKS
FROM dba_extents
WHERE segment_name = 'TEST_HWM';
当执行了shrink操作后,有意思的现象出现了。
我们来看看oracle是如何移动行数据的,这里的情况和move已经不太一样了。
我们知道,在move操作的时候,所有行的rowid都发生了变化,table所位于的block的区域也发生了变化,但是所有行物理存储的顺序都没有发生变化,所以我们得到的结论是,oracle以block为单位,进行了block间的数据copy。
那么shrink后,我们发现,部分行数据的rowid发生了变化,同时,部分行数据的物理存储的顺序也发生了变化,而table所位于的block的区域却没有变化,这就说明,shrink只移动了table其中一部分的行数据,来完成释放空间,而且,这个过程是在table当前所使用的block中完成的。
那么Oracle具体移动行数据的过程是怎样的呢?
我们根据这样的实验结果,可以来猜测一下:
Oracle是以行为单位来移动数据的。
Oracle从当前table存储的最后一行数据开始移动,从当前table最先使用的block开始搜索空间,所以,shrink之前,(9,es)被移动到blockAAA10z上,写到(1,aa)这行数据的后面,所以(9,es)的rowid发生改变。
然后是(6,es)这行数据,重复上述过程。
这是oracle从后向前移动行数据的大致遵循的规则,那么具体移动行数据的的算法是比较复杂的,包括向ASSM的table中insert数据使用block的顺序的算法也是比较复杂的,大家有兴趣的可以自己来研究,在这里我们不多做讨论。
在shrinktable的同时shrink这个table上的index:
altertablemy_objectsshrinkspacecascade;
同样地,这个操作只有当table上的index也是ASSM时,才能使用。
Move 和 Shrink 产生日志的对比
我们对比了同样数据量和分布状况的两张table,在move和shrink下生成的redosize(table上没有index的情况下):
SQL>SELECT tablespace_name, SEGMENT_SPACE_MANAGEMENT
FROM dba_tablespaces
WHERE tablespace_name IN ('HECV');
SQL>createtablemy_objectstablespaceHECVasselect*fromall_objectswhererownum<20000;
SQL>createtablemy_objects1tablespaceHECV_FRONTasselect*fromall_objectswhererownum<20000;
SQL>selectbytes/1024/1024fromuser_segmentswheresegment_name='MY_OBJECTS';
SQL>deletefrommy_objectswhereobject_namelike'%C%';
SQL>deletefrommy_objects1whereobject_namelike'%C%';
SQL>deletefrommy_objectswhereobject_namelike'%U%';
SQL>deletefrommy_objects1whereobject_namelike'%U%';
SQL>altertablemy_objectsenablerowmovement;
SQL>SELECT VALUE
FROM v$mystat, v$statname
WHERE v$mystat.statistic# = v$statname.statistic#
AND v$statname.name = 'redosize';
SQL>altertablemy_objectsshrinkspace;
SQL>SELECT VALUE
FROM v$mystat, v$statname
WHERE v$mystat.statistic# = v$statname.statistic#
AND v$statname.name = 'redosize';
SQL>altertablemy_objects1move;
SQL>SELECT VALUE
FROM v$mystat, v$statname
WHERE v$mystat.statistic# = v$statname.statistic#
AND v$statname.name = 'redosize';
对于tablemy_objects,进行shrink,产生了21148396–16762668=4385728,约4.2M的redo ;
对tablemy_objects1进行move,产生了21212908-21148396=64512,约63K的redosize。
结论:
与move比较起来,shrink的日志写要大得多。
Shrink的几点问题:
1. shrink后index是否需要rebuild:
因为shrink的操作也会改变行数据的rowid,那么,如果table上有index时,shrinktable后index会不会变为UNUSABLE呢?
我们来看这样的实验,同样构建my_objects的测试表:
createtablemy_objectstablespacehecvasselect*fromall_objectswhererownum<20000;
createindexi_my_objectsonmy_objects(object_id);
deletefrommy_objectswhereobject_namelike'%C%';
deletefrommy_objectswhereobject_namelike'%U%';
现在我们来shrinktablemy_objects:
SQL>altertablemy_objectsenablerowmovement;
SQL>altertablemy_objectsshrinkspace;
SQL>selectindex_name,statusfromuser_indexeswhereindex_name='I_MY_OBJECTS';
我们发现,tablemy_objects上的index的状态为VALID,估计shrink在移动行数据时,也一起维护了index上相应行的数据rowid的信息。
我们认为,这是对于move操作后需要rebuildindex的改进。
但是如果一个table上的index数量较多,我们知道,维护index的成本是比较高的,shrink过程中用来维护index的成本也会比较高。
2.shrink时对table的lock
在对table进行shrink时,会对table进行怎样的锁定呢?
当我们对tableMY_OBJECTS进行shrink操作时,查询v$locked_objects视图可以发现,tableMY_OBJECTS上加了row-X(SX) 的lock:
SQL>selectOBJECT_ID,SESSION_ID,ORACLE_USERNAME,LOCKED_MODEfromv$locked_objects;
OBJECT_IDSESSION_IDORACLE_USERNAMELOCKED_MODE
-------------------------------------------------
55422153DLINGER3
SQL>selectobject_idfromuser_objectswhereobject_name='MY_OBJECTS';
OBJECT_ID
----------
55422
那么,当table在进行shrink时,我们对table是可以进行DML操作的。
3.shrink对空间的要求
我们在前面讨论了shrink的数据的移动机制,既然oracle是从后向前移动行数据,那么,shrink的操作就不会像move一样,shrink不需要使用额外的空闲空间。