[一篇好文章]關於索引重用空間的總結測試

關於索引重用空間的總結測試:
因爲對於這方面的知識不是很瞭解,就做了些測試,希望大家討論一下!
如果我的觀點有誤,請大家熱心指正!

1。索引空間重用的一個例子
觀點:一旦索引使用過存儲區,就會永遠在那裏,並且只能被相同的值重用。(由此推測,空閒空間永遠不會返回索引結構,塊永遠不會重用)
結論:這種觀點是錯誤的,測試如下。
1)創建測試表:
SQL> create table t(x int,constraint t_pk primary key(x));

表已創建。

SQL> insert into t values(1);

已創建 1 行。

SQL> insert into t values(2);

已創建 1 行。

SQL> insert into t values(999999);

已創建 1 行。
SQL> set serveroutput on
SQL> exec show_space('T_PK',user,'INDEX');
Free Blocks.............................0
Total Blocks............................4
Total Bytes.............................32768
Unused Blocks...........................2
Unused Bytes............................16384
Last Used Ext FileId....................5
Last Used Ext BlockId...................634
Last Used Block.........................2

PL/SQL 過程已成功完成。

2)根據上面的觀點,如果從表T中刪除X=2,那個空間就永遠不會重用,除非再次插入數字2。目前這個索引使用2個空間塊,一個盤區地圖(extent map),一個是索引數據。如果刪除數據後索引條目從來不會重用,那麼如果連續不斷的插入和刪除,並且從來不用相同的值,索引應該不斷的增大。下面繼續測試
SQL> begin
2 for i in 2..9999
3 loop
4 delete from t where x=i;
5 commit;
6 insert into t values(i+1);
7 commit;
8 end loop;
9 end;
10 /

PL/SQL 過程已成功完成。
SQL> exec show_space('T_PK',user,'INDEX');
Free Blocks.............................0
Total Blocks............................4
Total Bytes.............................32768
Unused Blocks...........................2
Unused Bytes............................16384
Last Used Ext FileId....................5
Last Used Ext BlockId...................634
Last Used Block.........................2

PL/SQL 過程已成功完成。

索引的空間大小沒有變化,得出的結論表明索引中的空間重用了。
因爲索引能夠插入到適合的地方,如上面的例子,當數據2刪除後,數據2的slot可以被所有位於1-9999之間的數據使用。當數據3插入後刪除了,它的slot可以被所有位於1-9999之間的數據使用,這樣就實現了索引的空間重用。(這一部分一開始不理解,後來看了asktom上tom的書的英文說明,才明白了,看來expert one on one翻譯成了中文確實遜色很多)


 

2。索引空間重用的另外一個例子
索引空間從來不會返回到FREELIST上,直到塊上所有的entries都被釋放(通過delete,removed等),但是索引塊上的空閒空間可以得到最大的使用。
1)創建測試表和測試數據
SQL> create table emp(empno int constraint emp_pk primary key,
2 ename varchar2(30));

表已創建。

SQL> insert into emp select rownum, username from
2 all_users;

已創建13行。

SQL> analyze index emp_pk validate structure;

索引已分析

SQL> select lf_rows, del_lf_rows from index_stats;

LF_ROWS DEL_LF_ROWS
---------- -----------
13 0

這裏說明有13個葉子節點---13個索引行

2)刪除一部分數據
SQL> delete from emp where empno<=10;

已刪除10行。

SQL> analyze index emp_pk validate structure;

索引已分析

SQL> select lf_rows, del_lf_rows from index_stats;

LF_ROWS DEL_LF_ROWS
---------- -----------
13 10

這裏說明葉子節點還是13個,其中10個已經被標註“刪除”了---在表中這些刪除的葉子節點已經不指向實際的行

3)插入一部分數據
SQL> insert into emp
2 select rownum,lower(username)
3 from all_users
4 where rownum<=10;

已創建10行。

SQL> analyze index emp_pk validate structure;

索引已分析

SQL> select lf_rows, del_lf_rows from index_stats;

LF_ROWS DEL_LF_ROWS
---------- -----------
13 0

這裏顯示葉子節點爲13個,但是沒有標註“刪除”的葉子節點了,說明空間重用了。

4)繼續測試,使用不同的值來重用索引空間
SQL> delete from emp where empno <= 10;

已刪除10行。

SQL> commit;

提交完成。

SQL> insert into emp select -rownum,
2 lower(username) from all_users where rownum <= 10;

已創建10行。

SQL> analyze index emp_pk validate structure;

索引已分析
SQL> select lf_rows, del_lf_rows from index_stats;

LF_ROWS DEL_LF_ROWS
---------- -----------
13 0

這裏刪除後使用不同的值來插入,但是空間還是重用了。
當一行數據能夠使用索引中的slot時,索引塊的空間馬上就被重用了。
而且這種情況下是不使用FREELIST的(當索引BUILT或者REBUILT後,對於索引來說就不再使用FREELIST了)


3。索引空間不重用的情況
那麼什麼時候索引中的空間不重用那,是在下面這種索引有空洞的情況下:
在上面的情況下,原始的數字2使用(1-9999之間)的空間,將永遠保留在那個索引塊上,索引不會“接合”自己。這意味着,如果使用1到10000的值裝載一張表,然後隔行刪除(所有的偶數數字),在哪個列的索引中就會有5000個“洞”。只有重新插入適合塊的數據,並且那裏有洞時,空間纔會重用。
oracle不會嘗試“收縮”或壓縮索引,這可以通過ALTER INDEX REBUILD或COALESCE命令來完成。

測試如下:
1)創建測試表
SQL> create table t(x int);

表已創建。

SQL> begin
2 for i in 1..10000 loop
3 insert into t values(i);
4 commit;
5 end loop;
6 end;
7 /

PL/SQL 過程已成功完成。
SQL> select count(*) from t;

COUNT(*)
----------
10000

2)增加索引
SQL> alter table t add constraint t_pk primary key (x);

表已更改。

3)現在測量偶數行數據刪除前後利用的情況:
SQL> exec show_space('T_PK',user,'INDEX');
Free Blocks.............................0
Total Blocks............................24
Total Bytes.............................196608
Unused Blocks...........................2
Unused Bytes............................16384
Last Used Ext FileId....................5
Last Used Ext BlockId...................2
Last Used Block.........................2

PL/SQL 過程已成功完成。
SQL> begin
2 for i in 1..10000 loop
3 if mod(i,2)=0 then
4 delete from t where x=i;
5 end if;
6 end loop;
7 end;
8 /

PL/SQL 過程已成功完成。

SQL> select count(*) from t;

COUNT(*)
----------
5000
SQL> commit;

提交完成。

SQL> exec show_space('T_PK',user,'INDEX');
Free Blocks.............................0
Total Blocks............................24
Total Bytes.............................196608
Unused Blocks...........................2
Unused Bytes............................16384
Last Used Ext FileId....................5
Last Used Ext BlockId...................2
Last Used Block.........................2

PL/SQL 過程已成功完成。

數據刪除了一半,但是空間並沒有回到FREELIST上,因爲數據行中有空洞。說明在這種情況下,空間無法收回。
如果索引的數據和表的數據大小相當,而由於表的經常更新導致索引存在上面這種空洞,這時候就不難理解爲什麼索引佔用空間比表佔用空間大了。

4)測試使用ALTER INDEX COALESCE命令來收縮空間
SQL> alter index T_PK coalesce;

索引已更改。

SQL> exec show_space('T_PK',user,'INDEX');
Free Blocks.............................10
Total Blocks............................24
Total Bytes.............................196608
Unused Blocks...........................2
Unused Bytes............................16384
Last Used Ext FileId....................5
Last Used Ext BlockId...................2
Last Used Block.........................2

PL/SQL 過程已成功完成。

全部數據使用了22塊,刪除一半數據後,使用收縮命令釋放了10塊,差不多是一半,實現了空間的收回。

5)測試使用ALTER INDEX REBUILD命令來重建索引
SQL> exec show_space('T_PK',user,'INDEX');
Free Blocks.............................0
Total Blocks............................12
Total Bytes.............................98304
Unused Blocks...........................0
Unused Bytes............................0
Last Used Ext FileId....................5
Last Used Ext BlockId...................46
Last Used Block.........................4

PL/SQL 過程已成功完成。

重建索引實現了索引數據的重新分配,這種情況是最理想,所以在對錶經常更新,刪除後要重建索引。


4。關於索引空間自動回收的例子
觀點:索引空間從來不會自動“重新收回”,講的是一旦一個索引塊使用,就會永遠粘在索引結構的那個空間,並且只有插入的數據進入索引的那個位置時,它纔會重用。
結論:另一方面,如果使用1-10000的值裝載一張表,然後刪除其中值等於或小於5000的行,會發現從索引中清除的塊重新放到了索引的FREELIST中,這個空間可以完全重用。

1)創建測試表和測試數據,加入測試數據17920行,然後刪除連續的一半數據。
SQL> create table t(x int);

表已創建。

SQL> insert /*+ APPEND*/ into t select rownum from all_objects;

已創建4480行。
SQL> commit;

提交完成。

SQL> insert /*+ APPEND*/ into t
2 select rownum+cnt from t,(select count(*) cnt from t);

已創建4480行。
SQL> commit;

提交完成。
SQL> insert /*+ APPEND*/ into t
2 select rownum+cnt from t,(select count(*) cnt from t);

已創建8960行。
SQL> commit;

提交完成。

SQL> select count(*) from t;

COUNT(*)
----------
17920


2)增加索引
SQL> alter table t add constraint t_pk primary key (x);

表已更改。

3)現在測量大量連續數據刪除前後利用的情況:
SQL> exec show_space('T_PK',user,'INDEX');
Free Blocks.............................0
Total Blocks............................40
Total Bytes.............................327680
Unused Blocks...........................1
Unused Bytes............................8192
Last Used Ext FileId....................5
Last Used Ext BlockId...................546
Last Used Block.........................3

PL/SQL 過程已成功完成。

SQL> DELETE FROM T WHERE X<8960;

已刪除8959行。

SQL> COMMIT;

提交完成。

SQL> exec show_space('T_PK',user,'INDEX');
Free Blocks.............................17
Total Blocks............................40
Total Bytes.............................327680
Unused Blocks...........................1
Unused Bytes............................8192
Last Used Ext FileId....................5
Last Used Ext BlockId...................546
Last Used Block.........................3

PL/SQL 過程已成功完成。

可以看到,現在大約有一半的索引在FREELIST上,這意味着塊是空的(對索引FREELIST上的塊必須是空的,不像堆組織表中FREELIST上的塊)。

這主要證明了兩點:
1)只要出現的行能重用,索引塊上的空間就可以重用。
2)但一個索引塊爲空時,從索引結構中取走,以後可以重用。
當塊上有“空閒空間”並且在索引結構的表中時,塊時不可見的。在表中,可以看到空閒空間上的塊,即使其中有數據。在索引中,只能看到FREELIST上完全空的塊。塊上只要有一個索引條目(留有空閒空間)就不能清楚的看到。


5。對於索引中“空洞”空間的重新使用
1)創建測試表和數據,然後刪除偶數的數據,在索引中形成空洞。
SQL> create table t(x int);

表已創建。

SQL> begin
2 for i in 1..10000 loop
3 insert into t values(i);
4 commit;
5 end loop;
6 end;
7 /

PL/SQL 過程已成功完成。

SQL> select count(*) from t;

COUNT(*)
----------
10000

SQL> alter table t add constraint t_pk primary key(x);

表已更改。
SQL> exec show_space('T_PK',user,'INDEX');
Free Blocks.............................0
Total Blocks............................24
Total Bytes.............................196608
Unused Blocks...........................2
Unused Bytes............................16384
Last Used Ext FileId....................5
Last Used Ext BlockId...................626
Last Used Block.........................2

PL/SQL 過程已成功完成。

SQL> begin
2 for i in 1..10000 loop
3 if mod(i,2)=0 then
4 delete from t where x=i;
5 end if;
6 end loop;
7 end;
8 /

PL/SQL 過程已成功完成。
SQL> select count(*) from t;

COUNT(*)
----------
5000

SQL> commit;

提交完成。

SQL> exec show_space('T_PK',user,'INDEX');
Free Blocks.............................0
Total Blocks............................24
Total Bytes.............................196608
Unused Blocks...........................2
Unused Bytes............................16384
Last Used Ext FileId....................5
Last Used Ext BlockId...................626
Last Used Block.........................2

PL/SQL 過程已成功完成。

使用了22個block。

2)現在有5000條數據的空洞,插入範圍在1-10000之外的數據5000條,理論上應該不能使用“空洞”的空間
SQL> begin
2 for i in 10001..15000 loop
3 insert into t values(i);
4 commit;
5 end loop;
6 end;
7 /

PL/SQL 過程已成功完成。

SQL> select count(*) from t;

COUNT(*)
----------
10000

SQL> commit;

提交完成。
SQL> exec show_space('T_PK',user,'INDEX');
Free Blocks.............................2
Total Blocks............................32
Total Bytes.............................262144
Unused Blocks...........................0
Unused Bytes............................0
Last Used Ext FileId....................5
Last Used Ext BlockId...................46
Last Used Block.........................4

PL/SQL 過程已成功完成。

使用了30個block,可以看到這裏沒有使用空洞的空間。

3)插入符合“空洞”範圍的數據,看看是否能利用空間。
SQL> begin
2 for i in 1..10000 loop
3 if mod(i,2)=0 then
4 insert into t values(i);
5 end if;
6 end loop;
7 end;
8 /

PL/SQL 過程已成功完成。

SQL> select count(*) from t;

COUNT(*)
----------
15000

SQL> commit;

提交完成。
SQL> exec show_space('T_PK',user,'INDEX');
Free Blocks.............................1
Total Blocks............................32
Total Bytes.............................262144
Unused Blocks...........................0
Unused Bytes............................0
Last Used Ext FileId....................5
Last Used Ext BlockId...................46
Last Used Block.........................4

PL/SQL 過程已成功完成。

使用了31個block,可以看到當插入這些符合“空洞”條件的數據時,“空洞”的空間被重用了。


發佈了40 篇原創文章 · 獲贊 1 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章