簡單瞭解一下壓縮表

hanson發表於2019-03-31

資料被壓縮的原理在於,對於資料塊裡的重複值來說,只保留一份。儲存在資料塊裡的壓縮資料是自我包含的。也就是說,對於將壓縮的資料進行解壓來說,所有需要的資訊都在資料塊裡了,而不需要參考資料塊以外的其他資料。

資料塊裡的所有重複資料行和資料列都只存放一次,並放在資料塊的開始部分,這塊區域叫做資料塊的symbol table。也就是說,同一個資料塊裡放了兩個表的資料。這一點有點像class table。資料行裡如果存在重複的值,則該重複值去掉,並在原來的位置上放了一個很短的引用,指向symbol table。除了資料塊開始部分的symbol table以外,壓縮資料塊與普通的資料塊沒什麼兩樣。注意,在9i裡,壓縮表在建立以後就不能修改結構的,而10g則沒有這個限制了,但是不能對新增的列提供預設值。

 

對於壓縮資料塊的訪問來說,並沒有很消耗資源的解壓縮操作。這也就是說,在考慮是否要啟用壓縮時,並不需要考慮對於查詢效能的影響。只能在進行批量資料載入的時候,完成壓縮操作。在載入資料並壓縮過程中,CPU資源的消耗會有50%的增長。

 

只有當資料批量插入或載入的時候完成壓縮,這些操作包括:

·SQL*Loader的直接路徑載入。

·CREATE TABLE COMPRESS AS SELECT

·並行insert或帶有append提示的序列insert。你也可以發出ALTER TABLEMOVE COMPRESS,從而將表裡已有的資料進行壓縮。

 

對於壓縮表的update操作的效率極其低下,如果一個表要經常被update,則不應該對該表啟用壓縮。因為update時,需要先將資料解壓,然後對解壓後的資料進行更新,更新結束以後再次壓縮。

 

除了LOB以外的資料型別都可以被壓縮。另外,如果表裡的列超過255個,則壓縮被禁用。也提供對索引的壓縮,包括B樹索引和bitmap索引,以及IOT表。

 

來了解一下壓縮表的內部結構。

SQL> create table t_seed as

  2  select chr(trunc(dbms_random.value(65,67))) c1,chr(trunc(dbms_random.value(65,67))) c2,

  3  trunc(dbms_random.value(0,2)) c1flag,trunc(dbms_random.value(0,2)) c2flag

  4  from dba_objects where rownum<=1000;

 

表已建立。

 

SQL> create table t_comp_1 compress as

  2  select decode(c1flag,1,lpad(c1,10,c1),null) as c1,

  3  decode(c2flag,1,lpad(c2,10,c2),null) as c2 from t_seed;

 

表已建立。

 

SQL> select distinct dbms_rowid.rowid_relative_fno(rowid) as file#,

  2  dbms_rowid.rowid_block_number(rowid) as block# from t_comp_1 order by file#,block#;

 

     FILE#     BLOCK#

---------- ----------

         4       7604

         4       7605

 

SQL> alter system dump datafile 4 block 7604;

 

系統已更改。

 

data_block_dump,data header at 0xcc19a7c

===============

tsiz: 0x1f80

hsiz: 0x5ce

pbl: 0x0cc19a7c

bdba: 0x01001db4

     76543210

flag=-0------

ntab=2      這裡的2說明該資料塊裡有2個表,一個symbol table,一個實際的表。

nrow=725

frre=-1

fsbo=0x5ce

fseo=0x1147

avsp=0x3d

tosp=0x3d

    r0_9ir2=0x0

    mec_kdbh9ir2=0x9     這裡的9說明該資料塊裡重複值有9種情況。

                  76543210

    shcf_kdbh9ir2=----------

              76543210

    flag_9ir2=--R----C

        fcls_9ir2[3]={ 0 32768 32768 }

0x1c:pti[0] nrow=11 offs=0    這部分說明symbol table裡有6條記錄。

0x20:pti[1] nrow=717    offs=11   這部分說明該資料塊裡存放的實際表裡的記錄行數為717行。

0x24:pri[0] offs=0x1f61

0x26:pri[1] offs=0x1f5c

......................

tab 0, row 0, @0x1f61

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: *NULL*

col  1: *NULL*

bindmp: 00 a7 02 ff ff

tab 0, row 1, @0x1f5c

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: *NULL*

col  1: [10]  42 42 42 42 42 42 42 42 42 42

bindmp: 00 50 02 ff 0a

tab 0, row 2, @0x1f57

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: *NULL*

col  1: [10]  41 41 41 41 41 41 41 41 41 41

bindmp: 00 6c 02 ff 09

tab 0, row 3, @0x1f52

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  42 42 42 42 42 42 42 42 42 42

col  1: *NULL*

bindmp: 00 55 02 0a ff

tab 0, row 4, @0x1f4d

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  42 42 42 42 42 42 42 42 42 42

col  1: [10]  42 42 42 42 42 42 42 42 42 42

bindmp: 00 34 02 0a 0a

tab 0, row 5, @0x1f48

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  42 42 42 42 42 42 42 42 42 42

col  1: [10]  41 41 41 41 41 41 41 41 41 41

bindmp: 00 22 02 0a 09

tab 0, row 6, @0x1f43

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  41 41 41 41 41 41 41 41 41 41

col  1: *NULL*

bindmp: 00 5b 02 09 ff

tab 0, row 7, @0x1f3e

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  41 41 41 41 41 41 41 41 41 41

col  1: [10]  42 42 42 42 42 42 42 42 42 42

bindmp: 00 2c 02 09 0a

tab 0, row 8, @0x1f39

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  41 41 41 41 41 41 41 41 41 41

col  1: [10]  41 41 41 41 41 41 41 41 41 41

bindmp: 00 38 02 09 09

tab 0, row 9, @0x1f66

tl: 13 fb: --H-FL-- lb: 0x0  cc: 1

col  0: [10]  41 41 41 41 41 41 41 41 41 41

bindmp: 00 06 d2 41 41 41 41 41 41 41 41 41 41

tab 0, row 10, @0x1f73

tl: 13 fb: --H-FL-- lb: 0x0  cc: 1

col  0: [10]  42 42 42 42 42 42 42 42 42 42

bindmp: 00 06 d2 42 42 42 42 42 42 42 42 42 42

 

以上tab 0的部分就是所謂的symbol table裡所存放的記錄了。可以看到一共有11條記錄。實際就是對c1c2這兩列所存在的值的窮舉,如下所示:

可能的情況

C1

C2

1

AAAAAAAAAA

Null

2

BBBBBBBBBB

Null

3

Null

AAAAAAAAAA

4

Null

BBBBBBBBBB

5

AAAAAAAAAA

AAAAAAAAAA

6

AAAAAAAAAA

BBBBBBBBBB

7

BBBBBBBBBB

AAAAAAAAAA

8

BBBBBBBBBB

BBBBBBBBBB

9

Null

Null

 

而在該資料塊裡的每條記錄就是對上面這9種情況的描述,不過它窮舉出來的情況要更多點。

多出來的也就是最後兩條cc1記錄。也就是說有可能只壓縮一列。在我們這個例子裡,因為只有兩個列,所以沒有這種情況。如果表裡的列很多,則有可能只壓縮一列。

tab 0裡的每條記錄裡,記錄了c1列和c2列的可能的取值,同時還記錄了bindmp,這表示該種組合的一些後設資料。比如對於symbol table裡的row 4來說:

tab 0, row 4, @0x1f4d

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  42 42 42 42 42 42 42 42 42 42

col  1: [10]  42 42 42 42 42 42 42 42 42 42

bindmp: 00 34 02 0a 0a

前兩個位元組表示該資料塊裡有多少條記錄屬於該組合。這裡為十六進位制的00 34,換算成十進位制,也就是52。說明在該資料塊裡的717條記錄當中,有52條記錄的(c1,c2)=(BBBBBBBBBB, BBBBBBBBBB)02表示壓縮的列的個數為2個列。

我們來驗證一下:

SQL> select count(*) from(

  2  select c1,c2 from t_comp_1 where rownum<=717)

  3  where c1='BBBBBBBBBB' and c2='BBBBBBBBBB';

 

  COUNT(*)

----------

        52

 

tab 0以後的部分就是tab 1,這部分就是資料塊裡實際存放的記錄了。

tab 1, row 0, @0x1f34

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  42 42 42 42 42 42 42 42 42 42

col  1: *NULL*

bindmp: 2c 00 01 02 03

tab 1, row 1, @0x1f2f

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: *NULL*

col  1: *NULL*

bindmp: 2c 00 01 02 00

..................

轉儲的時候,進行了解壓。把c1c2的值都顯示出來。實際物理存放的值是:

bindmp: 2c 00 01 02 03

..................

也就是對每條記錄來說,只佔用了5個位元組。其中,2c 00應該表示symbol table的實體地址。02表示壓縮了2個欄位。03表示在symbol table裡的記錄行號。這裡的03也就是symbol table裡的row 3所記錄的c1c2的值。換句話說,該表裡的第一條記錄的(c1,c2)也就等於('BBBBBBBBBB', Null)

 

這時再建立一個壓縮表,該表裡多加兩個列:

SQL> create table t_comp_2 compress as

  2  select decode(c1flag,1,lpad(c1,10,c1),null) as c1,rownum id1,

  3  decode(c2flag,1,lpad(c2,10,c2),null) as c2,

  4  chr(trunc(dbms_random.value(65,80))) c3 from t_seed;

 

表已建立。

 

SQL> select distinct dbms_rowid.rowid_relative_fno(rowid) as file#,

  2  dbms_rowid.rowid_block_number(rowid) as block# from t_comp_2 order by file#,block#;

 

     FILE#     BLOCK#

---------- ----------

         4       6900

         4       6901

 

SQL> alter system dump datafile 4 block 6900;

 

系統已更改。

 

開啟轉儲檔案,可以看到下面的部分:

    r0_9ir2=0x0

    mec_kdbh9ir2=0x18

                  76543210

    shcf_kdbh9ir2=----------

              76543210

    flag_9ir2=--R---OC

        fcls_9ir2[4]={ 0 32768 32768 32768 }

        perm_9ir2[4]={ 0 3 1 2 }  多出了這一行。

這一行非常重要,因為在壓縮表中,oracle會修改列的排列順序。非壓縮表裡,列在物理存放的時候,按照資料字典裡記錄的順序,也就是col1col2col3....。比如:

SQL> desc t_comp_2

 名稱                                      是否為空? 型別

 ----------------------------------------- -------- ----------------------------

 C1                                                 VARCHAR2(2)

 ID1                                                NUMBER

 C2                                                 VARCHAR2(2)

 C3                                                 VARCHAR2(2)

也就是說,正常表來說,在資料塊裡存放列時,會這樣放:

col 0 XXXXXXX   ->表示放的是C1列的值

col 1 XXXXXXX   ->表示放的是ID1列的值

col 2 XXXXXXX   ->表示放的是C2列的值

col 3 XXXXXXX   ->表示放的是C3列的值

 

但是,在壓縮表裡,col 0已經不表示c1列的值了,col 1也不是ID1列的值。怎麼對應呢,就是從這裡的perm_9ir2[4]={ 0 3 1 2 }來對應。這裡的0312表示物理儲存時的列的下標。也就是說col 0對應資料字典裡的col 1,在這裡也就是C1列;col 3對應ID1col 1對應C2col 2對應C3

 

找到tab 1部分,比如:

tab 1, row 0, @0x1ea9

tl: 9 fb: --H-FL-- lb: 0x0  cc: 4

col  0: [10]  42 42 42 42 42 42 42 42 42 42  ->表示放的是C1列的值

col  1: *NULL*                               ->表示放的是C2列的值

col  2: [ 1]  49                             ->表示放的是C3列的值

col  3: [ 2]  c1 02                          ->表示放的是ID1列的值

bindmp: 2c 00 03 03 12 28 ca c1 02           ->這裡2c 00表示symbol table的地址。而03 來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/9842/viewspace-344546/,如需轉載,請註明出處,否則將追究法律責任。

相關文章