(C)我们来查询一下,看在插入一千万条记录后所访问的块数和查询所用时间:
SQL> SET TIMING ON
SQL> SET AUTOTRACE TRACEONLY
SQL> SELECT COUNT(*) FROM TEST_TAB;
ELAPSED: 00:01:03.05
EXECUTION PLAN
----------------------------------------------------------
--
0 SELECT STATEMENT OPTIMIZER=CHOOSE (COST=15056 CARD=1)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF ‘TEST_TAB‘ (COST=15056 CARD=10000
000)
STATISTICS
----------------------------------------------------------
0
RECURSIVE CALLS
0
DB BLOCK GETS
156310 CONSISTENT GETS
154239 PHYSICAL READS
0
REDO SIZE
379
BYTES SENT VIA SQL*NET TO CLIENT
503
BYTES RECEIVED VIA SQL*NET FROM CLIENT
2
SQL*NET ROUNDTRIPS TO/FROM CLIENT
0
SORTS (MEMORY)
0
SORTS (DISK)
1
ROWS PROCESSED
我们来看上面的执行计划,这句SQL总供耗时是:1分3秒.访问方式是采用全表扫描方式(FTS),逻辑读了156310个BLOCK,物理读了154239个BLOCK.
我们来分析一下这个表:
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(OWNNAME=> ‘TEST‘,
TABNAME=> ‘TEST_TAB‘,
PARTNAME=> NULL);END;
/
发现这个表目前使用的BLOCK有: 156532,未使用的BLOCK(EMPTY_BLOCKS)为:0,总行数为(NUM_ROWS):1000 0000
(D)接下来我们把此表的记录用DELETE方式删掉,然后再来看看SELECT COUNT(*) FROM TEST_TAB所花的时间:
DELETE FROM TEST_TAB;
COMMIT;
SQL> SELECT COUNT(*) FROM TEST_TAB;
ELAPSED: 00:01:04.03
EXECUTION PLAN
----------------------------------------------------------
0 SELECT STATEMENT OPTIMIZER=CHOOSE (COST=15056 CARD=1)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF ‘TEST_TAB‘ (COST=15056 CARD=1)
STATISTICS
----------------------------------------------------------
0
RECURSIVE CALLS
0 DB BLOCK GETS
156310 CONSISTENT GETS
155565 PHYSICAL READS
0 REDO SIZE
378 BYTES SENT VIA SQL*NET TO CLIENT
503 BYTES RECEIVED VIA SQL*NET FROM CLIENT
2 SQL*NET ROUNDTRIPS TO/FROM CLIENT
0 SORTS (MEMORY)
0 SORTS (DISK)
1 ROWS PROCESSED
大家来看,在DELETE表后,此时表中已没有一条记录,为什么SELECT COUNT(*) FROM TEST_TAB花的时间为1分4秒, 反而比有记录稍微长点,这是为什么呢?而且大家看,其逻辑读了156310个 BLOCK,跟之前有一千万行记录时差不多,ORACLE怎么会这么笨啊?
我们在DELETE表后再次分析表,看看有什么变化:
这时, TEST_TAB表目前使用的BLOCK是: 156532,未使用的BLOCK(EMPTY_BLOCKS)为:0,总行数为(NUM_ROWS)已变成:0
为什么表目前使的BLOCK数还是156532呢?
问题的根源就在于ORACLE的HWM.也就是说,在新增记录时,HWM会慢慢往上移,但是在删除记录后,HWM却不会往下移,也就是说,DELETE一千万条记录后,此表的HWM根本没移动,还在原来的那个位置,所以,HWM以下的块数同样也是一样的.ORACLE的全表扫描是读取ORACLE高水位标记下的所有BLOCK,也就是说,不管HWM下的BLOCK现在实际有没有存放数据,ORACLE都会一一读取,这样,大家可想而知,在我们DELETE表后,ORACLE读了大量的空块,耗去了大量的时间.
我们再来看DELETE表后段空间实际使用的状况:
SQL> EXEC SHOW_SPACE(‘TEST_TAB‘,‘TEST‘);
TOTAL BLOCKS............................164352
--总共164352块TOTAL BYTES.............................1346371584
UNUSED BLOCKS...........................7168
--有7168块没有用过,也就是在HWM上面的块数UNUSED BYTES............................58720256
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................158856
-- BLOCK ID 是针对数据文件来编号的,表示最后使用的一个EXTENT的第一个BLOCK的编号
LAST USED BLOCK.........................1024 --在最后使用的一个EXTENT 中一共用了1024块
PL/SQL PROCEDURE SUCCESSFULLY COMPLETED
总共用了164352块,除了一个SEGMENT HEADER,实际总共用了164351个块,有7168块从来没有使用过。LAST USED BLOCK表示在最后一个使用的EXTENT 中使用的BLOCK, 结合 LAST USED EXT BLOCK ID可以计算 HWM 位置 :
LAST USED EXT BLOCK ID + LAST USED BLOCK -1 = HWM 所在的数据文件的BLOCK编号
代入得出: 158856+1024-1=159879,这个就是HWM所有的BLOCK编号
HWM所在的块:TOTAL BLOCKS- UNUSED BLOCKS=164352-7168=157184,也就是说,HWM在第157184个块,其BLOCKID是159879
(E)结下来,我们再做几个试验:
第一步:执行ALTER TABLE TEST_TAB DEALLOCATE UNUSED;
我们看看段空间的使用状况:
SQL> EXEC SHOW_SPACE(‘TEST_TAB‘,‘TEST‘);
TOTAL BLOCKS............................157184
TOTAL BYTES.............................1287651328
UNUSED BLOCKS...........................0
UNUSED BYTES............................0
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................158856
LAST USED BLOCK.........................1024
此时我们再代入上面的公式,算出HWM的位置: 157184-0=157184 HWM所在的BLOCK ID是158856+1024-1=159879,跟刚刚的没有变化,也就是说执行ALTER TABLE TEST_TAB DEALLOCATE UNUSED后,段的高水位标记的位置没有改变,但是大家看看UNUSED BLOCKS变为0了,总的块数减少到157184,这证明,DEALLOCATE UNUSED为释放HWM上面的未使用空间,但是并不会释放HWM下面的自由空间,也不会移动HWM的位置.
第二步:我们再来看看执行ALTER TABLE TEST_TAB MOVE后段空间的使用状况:
SQL> EXEC SHOW_SPACE(‘TEST_TAB‘,‘TEST‘);
TOTAL BLOCKS............................8
TOTAL BYTES.............................65536
UNUSED BLOCKS...........................5
UNUSED BYTES............................40960
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................2632
LAST USED BLOCK.........................3
此时,总共用到的块数已变为8, 我们再代入上面的公式,算出HWM的位置: 8-5=3 HWM所在的BLOCK ID是2632+3-1=2634,
OK,我们发现,此时HWM的位置已经发生变化,现在HWM的位置是在第3个BLOCK,其BLOCK ID是2634,所有数据文件的ID是9(这个没有发生变化,数据文件还是原来的那个数据文件,只是释放了原来的自由空间),最后使用的块数也变为3,也就是说已经使用了3块,HWM就是在最后一个使用的块上,即第3个块上.大家可能会觉得奇怪,为什么释放空间后,未使用的块还有5个啊?也就是说HWM之上还是有5个已分配但从未使用的块.答案就跟HWM移动的规律有关.当我们在插入数据时,ORACLE首先在HWM之下的块当中定位自由空间(通过自由列表FREELIST),如果FREELIST当中没有自由块了,ORACLE就开始往上扩展,而HWM也跟着往上移,每5块移动一次.我们来看ORACLE的说明:
The high water mark is:
-Recorded in the segment header block
-Set to the beginning of the segment on the creation
-Incremented in five-block increments as rows are inserted
-Reset by the truncate command
-Never reset by the delete command
-Space above the high-water-mark can be reclaimed at the table level by using the following command:
ALTER TABLE DEALLOCATE UNUSED…
我们再来看看:SELECT COUNT(*) FROM TEST_TAB所花的时间:
SQL> SELECT COUNT(*) FROM TEST_TAB;
ELAPSED: 00:00:00.00
EXECUTION PLAN
----------------------------------------------------------
0 SELECT STATEMENT OPTIMIZER=CHOOSE
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF ‘TEST_TAB‘
STATISTICS
----------------------------------------------------------
0
RECURSIVE CALLS
0 DB BLOCK GETS
3
CONSISTENT GETS
0 PHYSICAL READS
0 REDO SIZE
378 BYTES SENT VIA SQL*NET TO CLIENT
503 BYTES RECEIVED VIA SQL*NET FROM CLIENT
2 SQL*NET ROUNDTRIPS TO/FROM CLIENT
0 SORTS (MEMORY)
0 SORTS (DISK)
1 ROWS PROCESSED
很快,不到1秒.
我们最后再来对表作一次分析, 此时这个表目前使用的BLOCK为: 0,未使用的BLOCK(EMPTY_BLOCKS)为:0,总行数为(NUM_ROWS):0
从中我们也可以发现,分析表和SHOW_SPACE显示的数据有点不一致.那么哪个是准的呢?其实这两个都是准的,只不过计算的方法有点不同.事实上,当你创建了一个对象如表以后,不管你有没有插入数据,它都会占用一些块,ORACLE也会给它分配必要的空间.同样,用ALTER TABLE MOVE释放自由空间后,还是保留了一些空间给这个表.
最后,我们再来执行TRUNCATE命令,截断这个表,看看段空间的使用状况:
TRUNCATE TABLE TEST_TAB;
SQL> EXEC SHOW_SPACE(‘TEST_TAB‘,‘TEST‘);
TOTAL BLOCKS............................8
TOTAL BYTES.............................65536
UNUSED BLOCKS...........................5
UNUSED BYTES............................40960
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................2632
LAST USED BLOCK.........................3
PL/SQL PROCEDURE SUCCESSFULLY COMPLETED
SQL>
我们发现TRUNCATE后和MOVE没有什么变化.
为了,最终验证一下我上面的观点,我再DROP一下表,然后新建这个表,看看这时在没有插入任何数据之前,是否ORACLE确实有给这个对象分配必要的空间:
DROP TABLE TEST_TAB;
CREATE TABLE TEST_TAB(C1 NUMBER(10),C2 CHAR(100)) TABLESPACE RAINNY;
SQL> EXEC SHOW_SPACE(‘TEST_TAB‘,‘TEST‘);
TOTAL BLOCKS............................8
TOTAL BYTES.............................65536
UNUSED BLOCKS...........................5
UNUSED BYTES............................40960
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................2112
LAST USED BLOCK.........................3
大家看,即使我没有插入任何一行记录,ORACLE还是给它分配了8个块.当然这个跟建表语句的INITIAL 参数及MINEXTENTS参数有关:请看TEST_TAB的存储参数:
S TORAGE
(
INITIAL 64K
MINEXTENTS 1
MAXEXTENTS UNLIMITED
);
也就是说,在这个对象创建以后,ORACLE至少给它分配一个区,初始大小是64K,一个标准块的大小是8K,刚好是8个BLOCK.
总结:
在9I中:
(1)如果MINEXTENT 可以使ALTER TABLE TABLENAME DEALLOCATE UNUSED将HWM以上所有没使用的空间释放
(2)如果MINEXTENT >HWM 则释放MINEXTENTS 以上的空间。如果要释放HWM以上的空间则使用KEEP 0。
ALTER TABLE TABLESNAME DEALLOCATE UNUSED KEEP 0;
(3) TRUNCATE TABLE DROP STORAGE(缺省值)命令可以将MINEXTENT 之上的空间完全释放(交还给操作系统),并且重置HWM。
(4)如果仅是要移动HWM,而不想让表长时间锁住,可以用TRUNCATE TABLE REUSE STORAGE,仅将HWM重置。
(5)ALTER TABLE MOVE会将HWM移动,但在MOVE时需要双倍的表空间,而且如果表上有索引的话,需要重构索引
(6)DELETE表不会重置HWM,也不会释放自由的空间(也就是说DELETE空出来的空间只能给对象本身将来的INSERT/UPDATE使用,不能给其它的对象使用)
在ORACLE 10G:
可以使用ALTER TABLE TEST_TAB SHRINK SPACE命令来联机移动HWM,
如果要同时压缩表的索引,可以发布:ALTER TABLE TEST_TAB SHRINK SPACE CASCADE
注意:在使用此命令时需要先使行可迁移row movement(具体见例子)。
与使用ALTER TABLE MOVE 不同的是执行此命令后并不需要重构索引。
Oracle 官方说明
Shrinking Database Segments Online
You use online segment shrink to reclaim fragmented free space below the high water mark in an Oracle Database segment. The benefits of segment shrink are these:
* Compaction of data leads to better cache utilization, which in turn leads to better online transaction processing (OLTP) performance.
* The compacted data requires fewer blocks to be scanned in full table scans, which in turns leads to better decision support system (DSS) performance.
Segment shrink is an online, in-place operation. DML operations and queries can be issued during the data movement phase of segment shrink. Concurrent DML operation are blocked for a short time at the end of the shrink operation, when the space is deallocated. Indexes are maintained during the shrink operation and remain usable after the operation is complete. Segment shrink does not require extra disk space to be allocated.
Segment shrink reclaims unused space both above and below the high water mark. In contrast, space deallocation reclaims unused space only above the high water mark. In shrink operations, by default, the database compacts the segment, adjusts the high water mark, and releases the reclaimed space.
Segment shrink requires that rows be moved to new locations. Therefore, you must first enable row movement in the object you want to shrink and disable any rowid-based triggers defined on the object.
Shrink operations can be performed only on segments in locally managed tablespaces with automatic segment space management (ASSM). Within an ASSM tablespace, all segment types are eligible for online segment shrink except these:
* IOT mapping tables
* Tables with rowid based materialized views
* Tables with function-based indexes
操作的过程:
SQL> create table demo as select * from dba_source;
Table created.
Elapsed: 00:00:05.83
SQL> select count(*) from demo;
COUNT(*)
----------
210992
Elapsed: 00:00:01.06
SQL> insert into demo select * from demo;
210992 rows created.
Elapsed: 00:00:59.83
SQL> commit;
Commit complete.
//得到一个40万条记录的表,下面来查看这个表空间分布情况。
SQL> exec show_space(‘demo‘,‘auto‘);
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.07
SQL> set serveroutput on
SQL> exec show_space(‘demo‘,‘auto‘);
Total Blocks............................9216
Total Bytes.............................75497472
Unused Blocks...........................768
Unused Bytes............................6291456
Last Used Ext FileId....................4
Last Used Ext BlockId...................8328
Last Used Block.........................256
一共有9216个数据块,HWM在9216-768=8448这个块.
也可以通过查看extents得到HWM=8*16+128*63+256=8192+256=8448
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
SQL> delete from demo where rownum<220000;
219999 rows deleted.
Elapsed: 00:00:40.99
SQL> commit;
Commit complete.
Elapsed: 00:00:00.01
SQL> exec show_space(‘demo‘,‘auto‘);
Total Blocks............................9216
Total Bytes.............................75497472
Unused Blocks...........................768
Unused Bytes............................6291456
Last Used Ext FileId....................4
Last Used Ext BlockId...................8328
Last Used Block.........................256
PL/SQL procedure successfully completed.
//删除操作后表的HWM没有变化,还是在第8448块这个位置。
Elapsed: 00:00:00.00
SQL> alter table demo shrink space;
alter table demo shrink space
*
ERROR at line 1:
ORA-10636: ROW MOVEMENT is not enabled
//先要enable row movement才能shrink
Elapsed: 00:00:00.09
SQL> alter table demo enable row movement;
Table altered.
Elapsed: 00:00:00.10
SQL> alter table demo shrink space;
Table altered.
Elapsed: 00:01:35.51
SQL> exec show_space(‘demo‘,‘auto‘);
Total Blocks............................3656
Total Bytes.............................29949952
Unused Blocks...........................0
Unused Bytes............................0
Last Used Ext FileId....................4
Last Used Ext BlockId...................3720
Last Used Block.........................72
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.02
//可以看到HWM降到了3656这个块上面!
参考资料:
http://tolywang.itpub.net/post/48/307529
http://www.itpub.net/viewthread.php?tid=205560
Oracle高水位线(HWM)及性能优化
标签:number 加载 append 索引 lead location 带来 material thread