1 名詞解釋與約定
資料表空間(Tablespace):為資料庫提供使用空間的邏輯結構,其對應物理結構是資料檔案,一個資料表空間可以包含多個資料檔案。
本地管理資料表空間(Locally Managed Tablespace簡稱LMT):8i以後出現的一種新的資料表空間的管理員模式,通過本地位元影像來管理資料表空間的空間使用。
字典管理資料表空間(Dictionary-Managed Tablespace簡稱DMT):8i以前包括以後都還可以使用的一種資料表空間管理員模式,通過資料字典管理資料表空間的空間使用。
段(Segment):資料庫一種邏輯結構,如表段,索引段,復原段等,段存在於資料表空間中,並對應一定的儲存空間。
區間,可以簡稱區(Extent):段的儲存可以分成一個或多個區間,每個區間佔用一定數量的資料區塊(block),在本地管理的資料表空間中,資料表空間的Extent就對應段的Extent。
塊(Block):資料庫最小的儲存單位,在本文中Block的大小約定為8192位元組。
位(Bit):本地管理資料表空間的空間管理單位,一個位可能等於一個區間,也可能多個位組成一個區間。
2 本地管理資料表空間
2.1 文法
在Oracle8I的版本中,Oracle推出了一種全新的資料表空間管理方式:本地化管理的資料表空間。所謂本地化管理,就是指Oracle不再利用資料字典表來記錄Oracle資料表空間裡面的區的使用狀況,而是在每個資料表空間的資料檔案的頭部加入了一個位元影像區,在其中記錄每個區的使用狀況。每當一個區被使用,或者被釋放以供重新使用時,Oracle都會更新資料檔案頭部的這個記錄,反映這個變化。
本地化管理的資料表空間的建立過程:
文法:CREATE TABLESPACE 資料表空間名字 DATAFILE '資料檔案詳細資料' [EXTENT MANAGEMENT { LOCAL {AUTOALLOCATE | UNIFORM [SIZE INTETER [K|M] ] } } ]
關鍵字EXTENT MANAGEMENT LOCAL 指定這是一個本地化管理的資料表空間。對於系統資料表空間,只能在建立資料庫的時候指定EXTENT MANGEMENT LOCAL,因為它是資料庫建立時建立的第一個資料表空間。
在8i中,字典管理還是預設的管理方式,當選擇了LOCAL關鍵字,即表明這是一個本地管理的資料表空間。當然還可以繼續選擇更細的管理方式:是AUTOALLOCATE 還是 UNIFORM.。若為AUTOALLOCATE,則表明讓Oracle來決定區塊的使用辦法;若選擇了UNIFORM,則還可以詳細指定每個區塊的大小,若不加指定,則為每個區使用1M大小。
2.2 本地管理優點
1. 本地化管理的資料表空間避免了遞迴的空間管理操作。而這種情況在資料字典管理的資料表空間是經常出現的,當資料表空間裡的區的使用狀況發生改變時,資料字典的表的資訊發生改變,從而同時也使用了在系統資料表空間裡的復原段。
2. 本地化管理的資料表空間避免了在資料字典相應表裡面寫入空閑空間、已使用空間的資訊,從而減少了資料字典表的競爭,提高了空間管理的並發性
3. 區的本地化管理自動跟蹤資料表空間裡的空閑塊,減少了手工合并自由空間的需要。
4. 資料表空間裡的區的大小可以選擇由Oracle系統來決定,或者由資料庫管理員指定一個統一的大小,避免了字典資料表空間一直頭疼的片段問題。
5. 從由資料字典來管理空閑塊改為由資料檔案的頭部記錄來管理空閑塊,這樣避免產生復原資訊,不再使用系統資料表空間裡的復原段。因為由資料字典來管理的話,它會把相關資訊記在資料字典的表裡,從而產生復原資訊。
由於這種資料表空間的以上特性,所以它支援在一個資料表空間裡邊進行更多的並行作業,並減少了對資料字典的依賴。
3 本地管理資料表空間管理機制
資料表空間是一種為段(表,索引等)提供空間的邏輯結構,所以,當在資料表空間中增加,刪除段的時候,資料庫就必須跟蹤這些空間的使用。
如下例所示,假定一個新建立的資料表空間包含了五個表
表一……表二……表三……表四……表五……未用空間
當我們刪除表四的時候,就有如下結果
表一……表二……表三……空閑空間段……表五……未用空間
很明顯,ORACLE需要有一個機制來管理資料表空間中各資料檔案的這些分配的或未分配的空間,為了跟蹤這些可以使用的空間(包括未分配使用的和可以重複使用的),對於每一個空間,我們必須知道:
1、這個可用空間位於什麼資料檔案
2、這個空間的尺寸是多大
3、如果它在用了,是哪一個段佔用的這個空間
直到8i之前,所有的資料表空間都是採用字典管理員模式,為了確保能儲存以上的資訊,ORACLE用了兩個資料字典表:UET$(已使用的區間)或FET$(空閑空間):
SQL> desc UET$
Name Type Nullable Default Comments
--------- ------ -------- ------- --------
SEGFILE# NUMBER
SEGBLOCK# NUMBER
EXT# NUMBER
TS# NUMBER
FILE# NUMBER
BLOCK# NUMBER
LENGTH NUMBER
SQL> desc FET$
Name Type Nullable Default Comments
------ ------ -------- ------- --------
TS# NUMBER
FILE# NUMBER
BLOCK# NUMBER
LENGTH NUMBER
查詢該表可以看到,每個使用空間或空閑空間(不一定是一個extent,可以是多個extent)都在該表中對應了一行。它的工作方式是當一個段被刪除的時候,ORACLE就移動UET$中相應的行到FET$,這個過程的發生是連續的,而且可能發生等待。當並發性很高的時候,資料字典的爭用就來了。另外有一個問題就是,當表的空間很不連續或資料表空間有大量的片段引起這兩個表的增大,那麼也就會引起資料庫效能上的下降。
本地管理資料表空間正是為瞭解決這一問題來的,在資料表空間的空間管理上,ORACLE將儲存資訊儲存在資料表空間的頭部的位元影像中,而不是儲存在資料字典中。通過這樣的方式,在分配回收空間的時候,資料表空間就可以獨立的完成操作也不用與其它對象關係。
下面就讓我們進入到本地管理資料表空間的內部,看看ORACLE是怎麼實現這一工作的。
4 用Uniform方式的本地管理資料表空間
4.1 當uniform size值太小時
SQL> create tablespace demo32 datafile '/oradata/ltest/demo32.dbf' size 10m extent management local uniform size 32k;
ORA-03249: Uniform size for auto segment space managed tablespace should have at least 5 blocks
注意: 我實驗環境block為8k, 所以uniform size至少為40k.
4.2 當storage參數中的initial為空白時
SQL> create tablespace demo64 datafile '/oradata/ltest/demo64.dbf' size 10m extent management local uniform size 64k;
Tablespace created
SQL> select a.initial_extent / 1024 "INIT_EXTENT(K)",
2 a.next_extent / 1024 "NEXT_EXTENT(K)"
3 from user_tablespaces a
4 where a.tablespace_name = 'DEMO64';
INIT_EXTENT(K) NEXT_EXTENT(K)
-------------- --------------
64 64
SQL> create table demotab64(x number) tablespace demo64;
Table created
SQL> select a.table_name,
2 a.initial_extent / 1024 "INIT_EXTENT(K)",
3 a.next_extent / 1024 "NEXT_EXTENT(K)"
4 from user_tables a
5 where a.table_name = 'DEMOTAB64';
TABLE_NAME INIT_EXTENT(K) NEXT_EXTENT(K)
-------------------------- -------------- --------------
DEMOTAB64 64 64
注意: 建表時沒有儲存參數initial時, 初始化區與下一個區的大小都是64k, 與uniform size的大小一樣的.
SQL> select a.bytes / 1024 "INIT_EXTENT(K)" from user_extents a where a.segment_name = 'DEMOTAB64' and a.extent_id = 0;
INIT_EXTENT(K)
--------------
64
SQL> select count(*) from user_extents where segment_name = 'DEMOTAB64';
COUNT(*)
----------
1
注意: 在該段中, 產生一個區.
4.3 當initial < uniform size時
SQL> create table demotab64_1(x number) tablespace demo64 storage (initial 1K next 5k);
Table created
SQL> select a.table_name,
2 a.initial_extent / 1024 "INIT_EXTENT(K)",
3 a.next_extent / 1024 "NEXT_EXTENT(K)"
4 from user_tables a
5 where a.table_name = 'DEMOTAB64_1';
TABLE_NAME INIT_EXTENT(K) NEXT_EXTENT(K)
--------------------------- -------------- --------------
DEMOTAB64_1 16 64
注意: 此時INIT_EXTENT為16, 不是initial參數的1.
SQL> select a.bytes / 1024 "INIT_EXTENT(K)" from user_extents a where a.segment_name = 'DEMOTAB64_1' and a.extent_id = 0;
INIT_EXTENT(K)
--------------
64
SQL> select count(*) from user_extents where segment_name = 'DEMOTAB64_1';
COUNT(*)
----------
1
4.4 當initial > uniform size時
SQL> create table demotab64_200(x number) tablespace demo64 storage (initial 200K next 20k);
Table created
SQL> select a.table_name,
2 a.initial_extent / 1024 "INIT_EXTENT(K)",
3 a.next_extent / 1024 "NEXT_EXTENT(K)"
4 from user_tables a
5 where a.table_name = 'DEMOTAB64_200';
TABLE_NAME INIT_EXTENT(K) NEXT_EXTENT(K)
-------------------------- -------------- --------------
DEMOTAB64_200 200 64
注意: initial > uniform size時, 初始化區的大小initial的大小.
SQL> select a.bytes / 1024 "INIT_EXTENT(K)" from user_extents a where a.segment_name = 'DEMOTAB64_200' and a.extent_id = 0;
INIT_EXTENT(K)
--------------
64
SQL> select count(*) from user_extents where segment_name = 'DEMOTAB64_200';
COUNT(*)
----------
4
注意: 此時分配的區已經不是1個了, 是4個. 在這種情況下initial就有起做作用. 分配區的數量為: 取整(initial/uniform size) + 1
結論: 在uniform size時, initial不管為多少時, 這個段的每一個區大小都為uniform size的大小.
5 用autoallocate方式的本地管理資料表空間
5.1 當storage參數中的initial為空白時
SQL> create tablespace demoa datafile '/oradata/ltest/demoa.dbf' size 10m extent management local autoallocate;
Tablespace created
SQL> select a.initial_extent / 1024 "INIT_EXTENT(K)",
2 a.next_extent / 1024 "NEXT_EXTENT(K)"
3 from user_tablespaces a
4 where a.tablespace_name = 'DEMOA';
INIT_EXTENT(K) NEXT_EXTENT(K)
-------------- --------------
64
SQL> create table demoatab(x number) tablespace demoa;
Table created
SQL> select a.table_name,
2 a.initial_extent / 1024 "INIT_EXTENT(K)",
3 a.next_extent / 1024 "NEXT_EXTENT(K)"
4 from user_tables a
5 where a.table_name = 'DEMOATAB';
TABLE_NAME INIT_EXTENT(K) NEXT_EXTENT(K)
------------------------- -------------- --------------
DEMOATAB 64
SQL> select count(*) from user_extents where segment_name = 'DEMOATAB';
COUNT(*)
----------
1
此後不斷增加資料,可以發現:
SQL> select a.segment_name, a.bytes, a.blocks from user_extents a where a.segment_name = 'DEMOATAB';
SEGMENT_NA BYTES BLOCKS
---------- ---------- ----------
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 65536 8
DEMOATAB 1048576 128
DEMOATAB 1048576 128
DEMOATAB 1048576 128
DEMOATAB 1048576 128
DEMOATAB 1048576 128
DEMOATAB 1048576 128
DEMOATAB 1048576 128
23 rows selected
當自動分配時,發現開始第一個區分配8個塊(64K), 到17區開始, 每個區分配128個塊(大小1M). 我做過實驗當initial足夠大時, 第一個區的大小不一定都是64K, 可以是1M, 8M, 64M 甚至是256M.
5.2 當initial < uniform size時
SQL> create table demoatab_1(x number) tablespace demoa storage (initial 1K next 5k);
Table created
SQL> select a.table_name,
2 a.initial_extent / 1024 "INIT_EXTENT(K)",
3 a.next_extent / 1024 "NEXT_EXTENT(K)"
4 from user_tables a
5 where a.table_name = 'DEMOATAB_1';
TABLE_NAME INIT_EXTENT(K) NEXT_EXTENT(K)
-------------------------- -------------- --------------
DEMOATAB_1 16
SQL> select a.bytes / 1024 "INIT_EXTENT(K)" from user_extents a where a.segment_name = 'DEMOATAB_1' and a.extent_id = 0;
INIT_EXTENT(K)
--------------
64
SQL> select count(*) from user_extents where segment_name = 'DEMOATAB_1';
COUNT(*)
----------
1
5.3 當initial > uniform size時
SQL> create table demoatab_200(x number) tablespace demoa storage (initial 200K next 5k);
Table created
SQL> select a.table_name,
2 a.initial_extent / 1024 "INIT_EXTENT(K)",
3 a.next_extent / 1024 "NEXT_EXTENT(K)"
4 from user_tables a
5 where a.table_name = 'DEMOATAB_200';
TABLE_NAME INIT_EXTENT(K) NEXT_EXTENT(K)
--------------------------- -------------- --------------
DEMOATAB_200 200
SQL> select a.bytes / 1024 "INIT_EXTENT(K)" from user_extents a where a.segment_name = 'DEMOATAB_1' and a.extent_id = 0;
INIT_EXTENT(K)
--------------
64
SQL> select count(*) from user_extents where segment_name = 'DEMOATAB_200';
COUNT(*)
----------
4
結論: 1. ORACLE通過強制性的手段使本地管理資料表空間中的所有Extent是同樣大小的, 儘管可能自訂了不同的儲存參數. 2. 在自動分配的本地管理的資料表空間中, 區間尺寸可能由以下尺寸組成64K, 1M, 8M, 64M 甚至是256M. 但是不管多大, 都有一個通用尺寸64k, 所以64K就是該資料表空間的位大小.
6 檢查uet$與fet$是否有資料
SQL> select file#, name from v$datafile;
FILE# NAME----------------------------------------
1 /oradata/LTEST/datafile/o1_mf_system_79q5214w_.dbf
2 /oradata/LTEST/datafile/o1_mf_undotbs1_79q521ct_.dbf
3 /oradata/LTEST/datafile/o1_mf_sysaux_79q52169_.dbf
4 /oradata/LTEST/datafile/o1_mf_users_79q521do_.dbf
5 /oradata/LTEST/datafile/o1_mf_example_79q55jt4_.dbf
6 /oradata/LTEST/datafile/o1_mf_bigtbs_7ct5vw4x_.dbf
12 /oradata/ltest/demo64.dbf
13 /oradata/ltest/demo1024.dbf
14 /oradata/ltest/demoa.dbf
9 rows selected
可以檢查uet$與fet$
SQL> select count(*) from uet$ where file# = 12;
COUNT(*)
----------
0
SQL> select count(*) from fet$ where file# = 12;
COUNT(*)
----------
0
採用本地管理的資料表空間, 這兩張視圖中沒有資料. 下面就通過Dump塊的資訊, 來進一步分析本地管理資料表空間的特點.
7 Dump資料檔案中位元影像資訊(第3個塊到第8個塊)
7.1 dump第三個塊