1. 程式人生 > 其它 >Oracle中的段(r10筆記第81天)

Oracle中的段(r10筆記第81天)

Oracle的體系結構中,關於儲存結構大家應該都很熟悉了。

估計下面這張圖大家都看得熟悉的不能再熟悉了。

簡單來說,裡面的一個重要概念就是段,如果是開發同學,可能每次聽到這裡都會有些模糊,好像懂,好像不懂。n年前給開發同學內部培訓的時候,有開發同事就百思不得其解,我當時按照書上的原話也沒有收服它們,同樣的話出自DBA的口中似乎多了一些信服的味道。多年之後,還有同學問起我這個問題的時候,讓我想起了曾經苦苦學習Oracle的那些時光,我簡單說說我的理解。

首先老三篇的內容我們換一種玩法,如果是一個11gR2的預設資料庫環境,下面的這個語句會建立幾個段。

create table test_seg (id number primary key,name varchar2(30),memo clob);

這個問題問住了不少的人,而似乎我也聽到了不少意料之中的答案,如果認為是1個的,就是理解建立了一個表,那就是一個段了,這個段型別是TABLE,如果認為是2個的,那就是看到了裡面有一個主鍵,那就是對應一個索引段,如果認為是3個的,那就是看到了LOB欄位,不過還差一步,LOB其實是會建立兩個段,一個是LOB段,一個是LOB索引段,結構化的資料是Oracle的強項,而如果加入半結構化的資料處理,就得額外花些功夫。而如果我們在11gR2的環境中,我們耳濡目染的而一個特性,延遲段建立,存在一個數據庫引數segment_deferred_creation預設為true,即開啟了延遲段建立,所以上面的語句不會建立任何的段。

SQL> select segment_name,segment_type from user_segments;
no rows selected

而我們插入一部分的資料,我們就插入近50萬資料吧。insert into test_seg select level,'obj'||level,'' from dual connect by level<500000;

這樣一來,問題就回歸了,是4個段,其中SYS_IL開頭的段是索引段,剩下SYS_開頭的兩個是LOB相關的段。

select segment_name,segment_type from user_segments;
SEGMENT_NAME                   SEGMENT_TYPE
------------------------------ ------------------------------------
TEST_SEG                       TABLE
SYS_IL0000089494C00003$$       LOBINDEX
SYS_C0011978                   INDEX
SYS_LOB0000089494C00003$$      LOBSEGMENT

怎麼去理解這個段呢。

我們還是求助於user_segments來看看。

SQL> select segment_name,extents,blocks from user_segments
SEGMENT_NAME                      EXTENTS     BLOCKS
------------------------------ ---------- ----------
TEST_SEG                               26       1408
SYS_IL0000089494C00003$$                1          8
SYS_C0011978                           23       1024
SYS_LOB0000089494C00003$$               1          8

可以看到段TEST_SEG存在26個區塊,含有1408個數據塊。這些都是概覽的資訊。

如果想得到更細緻一些的資訊呢,那就是user_extents了。我們簡化一下,只是檢視segment_type為TABLE的區塊資訊。

SQL> select segment_name,segment_type,extent_id,bytes,blocks from user_extents 
 where segment_name='TEST_SEG' order by segment_name,extent_id ;

TEST_SEG             TABLE    0      65536          8
TEST_SEG             TABLE    1      65536          8
TEST_SEG             TABLE    2      65536          8
TEST_SEG             TABLE    3      65536          8
TEST_SEG             TABLE    4      65536          8
T

這裡我們可以看到段級別對應的區是0-25,和user_segments中看到的26個區資料是一致的。

而這些區塊的ID其實就是一個邏輯的編號,是基於段級別的劃分。

同樣上面的語句,我們簡單改一下,檢視segment_type為INDEX的區塊資訊。如下:

SEGMENT_NAME         SEGMENT_TYPE          EXTENT_ID      BYTES     BLOCKS
-------------------- -------------------- ---------- ---------- ----------
SYS_C0011978         INDEX                         0      65536          8
SYS_C0011978         INDEX                         1      65536          8
SYS_C0011978         INDEX                         2      65536          8
SYS_C0011978         INDEX                         3      65536          8
SYS_C0011978         INDEX                         4      65536          8
SYS_C0011978         INDEX                         5      65536          8
SYS_C0011978         INDEX                         6      65536          8
SYS_C0011978         INDEX                         7      65536          8

可以很明顯看到區塊的ID也是從0開始,比如0-7

這裡就需要明白,這裡的區塊0是相對於這個segment_type為INDEX的segment為SYS_C0011978而言的。所以這裡的值是一個相對的值,而不是絕對的。

而絕對的位置怎麼查詢呢,這得依賴於物理結構,能夠定位的一個利器就是ROWID,我們取出1行資料來看看。

SQL> select dbms_rowid.ROWID_RELATIVE_FNO(rowid) as  file#,dbms_rowid.ROWID_BLOCK_NUMBER(rowid) as  block#,dbms_rowid.ROWID_ROW_NUMBER(rowid) as row#,a.* from test.test_seg  a where rownum<2;
     FILE#     BLOCK#       ROW#         ID NAME                 MEMO
---------- ---------- ---------- ---------- -------------------- ----------
         4     134803          0        468 obj468

可以看到是4號資料檔案,對應的資料塊是134803

要知道一個數據塊裡存放的資料基本上不是1行資料,而是相關的多行資料。

我們就可以做一個dump來看看。

alter system dump datafile 4 block 134803; 然後檢視 select *from v$diag_info; 來得到trace檔案的路徑。

可以通過trace檔案看到下面的內容:

 tab 0, row 0, @0x1f8a
tl: 14 fb: --H-FL-- lb: 0x1  cc: 2
col  0: [ 3]  c2 05 45
col  1: [ 6]  6f 62 6a 34 36 38
tab 0, row 1, @0x1f7c
tl: 14 fb: --H-FL-- lb: 0x1  cc: 2
col  0: [ 3]  c2 05 46
col  1: [ 6]  6f 62 6a 34 36 39

目前存在3個欄位,但是因為當前的LOB欄位為空,就直接延遲建立了,只顯示出來了兩個欄位。

而繼續做一個update操作,提交事務。

SQL> update test_seg  set memo='aaa' where  rownum=1;
commit;

可以通過trace看到裡面的內容已經大大不同,一個最大的變化就是LOB段的延遲建立情況,這個時候修改LOB段資料,所以就會重新初始化。

tab 0, row 0, @0x690
tl: 57 fb: --H-FL-- lb: 0x2  cc: 3
col  0: [ 3]  c2 05 45
col  1: [ 6]  6f 62 6a 34 36 38
col  2: [42]
 00 54 00 01 02 0c 80 00 00 02 00 00 00 01 00 00 00 12 ef 45 00 16 09 00 00
 00 00 00 00 06 00 00 00 00 00 01 00 61 00 61 00 61
LOB
Locator:
  Length:        84(42)
  Version:        1
  Byte Length:    2
  LobID: 00.00.00.01.00.00.00.12.ef.45
  Flags[ 0x02 0x0c 0x80 0x00 ]:
    Type: CLOB 
    Storage: BasicFile
    Enable Storage in Row 
    Characterset Format: IMPLICIT
    Partitioned Table: No
    Options: VaringWidthReadWrite 
  Inode: 
    Size:     22
    Flag:     0x09 [ Valid DataInRow ]
    Future:   0x00 (should be '0x00')
    Blocks:   0
    Bytes:    6
    Version:  00000.0000000001
    Inline data[6]
Dump of memory from 0x00007FBE38B88127 to 0x00007FBE38B8812D
7FBE38B88120          00010000 00610061 02002C61      [....a.a.a,..]
tab 0, row 1, @0x1f7c
tl: 14 fb: --H-FL-- lb: 0x0  cc: 2
col  0: [ 3]  c2 05 46
col  1: [ 6]  6f 62 6a 34 36 39  

所以對於段來說,裡面的資訊對應的都是邏輯的概念,地址是邏輯地址,是相對的,而非絕對的。切莫認為一個區可以同時屬於多個段。