Oracle 11g新特性之SecureFiles

idba發表於2008-03-26

SecureFiles:新 LOB

瞭解如何使用新一代 LOB:SecureFiles。SecureFiles 集外部檔案和資料庫 LOB 方法的優點於一身,可以儲存非結構化資料,允許加密、壓縮、重複消除等等。

資料庫駐留 BLOBS 或 OS 檔案

您在 Oracle 資料庫中儲存什麼?通常,您會以關係格式儲存資料以便於對映到某些定義模式的型別,或者以客戶姓名、帳戶餘額、狀態程式碼等定義的資料型別儲存資料。但是,以非結構化或半結構化格式儲存資訊的需求也日益增加。例如,照片、字處理文件、電子表格、XML 檔案等等。這些型別的資料如何儲存?

通常有兩種方法:這些資料作為 LOB 欄位(BLOB 用於儲存二進位制資料,CLOB 用於儲存字元資料)儲存在資料庫中,或者通過引用儲存在資料庫中的檔案儲存在 OS 檔案中。

每種方法都各具優缺點。OS 檔案可以由 OS 和日誌檔案系統快取以加速崩潰後的恢復。由於可進行壓縮,因此 OS 檔案佔用的空間通常也比資料庫中的資料要少。

還有一些工具可以智慧地識別檔案模式並消除重複從而提高儲存效率;但是 OS 檔案位於資料庫外部,因此資料庫屬性不適用於它們。這些檔案不進行備份、細粒度安全性不適用於它們,此類檔案不是事務的一部分 — 因此 Oracle 資料庫固有的讀取一致性等該概念不適用於它們。

如果能集兩種方法的優點於一身會怎樣?Oracle 資料庫 11g 中的 SecureFiles 為您提供了答案。SecureFiles 是資料庫中一個全新的基礎架構,可提供最佳的資料庫駐留 LOB 和 OS 檔案效能。讓我們看一看這種方案的實現方式。(順便說明的是,傳統 LOB 仍然以 BasicFiles 格式提供。)

實際示例

通過一個簡單的示例來介紹 SecureFiles 概念或許是一種最佳方式。假設您要開發一個合同管理系統,在該系統中您希望將所有合同的副本都放在一個表中。掃描的文件通常是 PDF 檔案而非文字。某些可能是 MS Word 文件或者甚至是掃描的照片。這是 BLOB 的最佳使用案例,因為列必需能夠支援二進位制資料。

通常,在 Oracle 資料庫 11g 推出之前,您可能會按照以下方式定義表:

create table contracts_basic
(
        contract_id     number(12),
        contract_name   varchar2(80),
        file_size       number,
        orig_file       blob
)
tablespace users
lob (orig_file)
(
        tablespace users
        enable storage in row
        chunk 4096
        pctversion 20
        nocache
        nologging
);
\

實際的檔案以二進位制格式儲存在 ORIG_FILE 列中。各種引數指明在操作期間 LOB 不應快取並記入日誌,應按錶行儲存,塊大小應為 4KB 並儲存在表空間 USERS 中。由於您沒有明確指定,因此 LOB 在 Oracle 資料庫 11g 中以常規格式 (BasicFiles) 儲存。

如果您將 LOB 儲存為 SecureFile,只需在建立表時放入一個子句 store as securefile,如下所示:

create table contracts_sec 
(
        contract_id     number(12),
        contract_name   varchar2(80),
        file_size       number,
        orig_file       blob
)
tablespace users
lob (orig_file)
store as securefile
(
        tablespace users
        enable storage in row
        chunk 4096
        pctversion 20
        nocache
        nologging
)
/

要建立 SecureFile LOB,您需要滿足兩個條件,而這兩個條件都是預設設定,因此您可能已經滿足。

  • 初始化引數 db_securefile 應設為 permitted(預設設定)。我會在後面部分說明該引數的作用。
  • 在其中建立 securefile 的表空間應啟用自動段空間管理 (ASSM)。在 Oracle 資料庫 11g 中,表空間建立的預設模式為 ASSM,因此該表空間可能已經這樣設定。如果沒有,則需要在一個新的 ASSM 表空間上建立 SecureFile。

表建立完成後,您可以載入資料,方式與 11g 之前的常規 LOB (BasicFile) 相同。不需要更改應用程式,也不需要記住某些特殊的語法。

下面是一個載入該表的小程式。

declare
    l_size      number;  
    l_file_ptr  bfile;   
    l_blob      blob;    
begin
    l_file_ptr := bfilename('SECFILE', 'contract.pdf'); 
    dbms_lob.fileopen(l_file_ptr); 
    l_size := dbms_lob.getlength(l_file_ptr); 
    for ctr in 1 .. 100 loop 
        insert into contracts_sec 
        (        
            contract_id, 
            contract_name, 
            file_size,   
            orig_file    
        )        
        values   
        (        
            ctr,         
            'Contract '||ctr, 
            null,        
            empty_blob() 
        )        
        returning orig_file into l_blob;  
        dbms_lob.loadfromfile(l_blob, l_file_ptr, l_size); 
    end loop; 
    commit; 
    dbms_lob.close(l_file_ptr); 
end;
/

該程式將 contract.pdf 檔案分 100 次載入表的 100 個行中。您應該已經為儲存 contract.pdf 檔案的 OS 目錄定義了一個名為 SECFILE 的目錄物件。下面的示例指明 contract.pdf 檔案在 /opt/oracle 中的位置。

SQL> create directory secfile for ’/opt/oracle’;

將 LOB 儲存為 SecureFile 後,您就可以獲得很多能優化操作的特性。下面就是其中一些很有用的特性。

重複消除

重複消除可能是 SecureFiles 中最受歡迎的特性,它由於 OS 檔案在某些高階檔案系統中相對於資料庫駐留 BLOB 的優勢而倍受推崇。假設一個表有五個記錄,每個記錄各有一個 BLOB。其中三個 BLOB 是完全相同的。如果能夠只儲存該 BLOB 一次而在其他兩個記錄上只儲存對該副本的引用將極大地減少空間消耗。這在 OS 在檔案中是可行的,但在 Oracle 資料庫 10g LOB 中則無法實現。但是如果使用 SecureFiles,只需通過一個稱為重複消除的屬性即可輕鬆搞定。可以在表建立時指定該屬性,也可以在以後將其修改為:

SQL> alter table contracts_sec
  2  modify lob(orig_file)
  3  (deduplicate)
  4  /
 
Table altered.

使用重複消除後,資料庫計算每行中各列的雜湊值並進行相互比較。如果有匹配的雜湊值,則儲存該雜湊值而不是實際的 BLOB。新記錄插入時會計算其雜湊值,如果其雜湊值與其他雜湊值匹配,則插入雜湊值;否則儲存實際的值。

下面,我們來看看進行重複消除過程後的空間節省情況。可以通過程式包 DBMS_SPACE 檢查 LOB 段中的空間消耗。下面是一個顯示空間消耗的程式:

declare  
    l_segment_name          varchar2(30); 
    l_segment_size_blocks   number; 
    l_segment_size_bytes    number; 
    l_used_blocks           number;  
    l_used_bytes            number;  
    l_expired_blocks        number;  
    l_expired_bytes         number;  
    l_unexpired_blocks      number;  
    l_unexpired_bytes       number;  

begin
    select segment_name 
    into l_segment_name 
    from dba_lobs 
    where table_name = 'CONTRACTS_SEC'; 
        dbms_output.put_line('Segment Name=' || l_segment_name);
 
    dbms_space.space_usage( 
        segment_owner           => 'ARUP',  
        segment_name            => l_segment_name, 
        segment_type            => 'LOB', 
        partition_name          => NULL, 
        segment_size_blocks     => l_segment_size_blocks, 
        segment_size_bytes      => l_segment_size_bytes, 
        used_blocks             => l_used_blocks, 
        used_bytes              => l_used_bytes, 
        expired_blocks          => l_expired_blocks, 
        expired_bytes           => l_expired_bytes, 
        unexpired_blocks        => l_unexpired_blocks, 
        unexpired_bytes         => l_unexpired_bytes 
    );   


    dbms_output.put_line('segment_size_blocks       => '||  l_segment_size_blocks);
    dbms_output.put_line('segment_size_bytes        => '||  l_segment_size_bytes);
    dbms_output.put_line('used_blocks               => '||  l_used_blocks);
    dbms_output.put_line('used_bytes                => '||  l_used_bytes);
    dbms_output.put_line('expired_blocks            => '||  l_expired_blocks);
    dbms_output.put_line('expired_bytes             => '||  l_expired_bytes);
    dbms_output.put_line('unexpired_blocks          => '||  l_unexpired_blocks);
    dbms_output.put_line('unexpired_bytes           => '||  l_unexpired_bytes);
end;
/

該指令碼顯示 LOB 的各種與空間有關的統計資訊。進行重複消除過程之前,輸出如下:

Segment Name=SYS_LOB0000070763C00004$$
segment_size_blocks     => 1072
segment_size_bytes      => 8781824
used_blocks             => 601
used_bytes              => 4923392
expired_blocks          => 448
expired_bytes           => 3670016
unexpired_blocks        => 0
unexpired_bytes         => 0

使用重複消除後:

Segment Name=SYS_LOB0000070763C00004$$
segment_size_blocks     => 1456
segment_size_bytes      => 11927552
used_blocks             => 7
used_bytes              => 57344
expired_blocks          => 127
expired_bytes           => 1040384
unexpired_blocks        => 1296
unexpired_bytes         => 10616832

僅以上輸出中的一個度量標準就足以說明問題:used_bytes。它顯示了 LOB 列儲存的位元組數。使用重複消除之前,需要佔用 4,923,392 位元組或 5MB,但使用重複消除後,該值縮減為 57,344 位元組或大約 57KB,差不多是原始值的 1%。這是因為重複消除過程發現了 100 個具相同值的行(回憶一下,我們將同一個值放到了所有行的 LOB 列中)。重複消除過程只保留了一行而使其他行成為指標。

您也可以反向進行該重複消除過程:

SQL> alter table contracts_sec
  2  modify lob(orig_file)
  3  (keep_duplicates)
  4  /
 
Table altered.

之後,如果您再檢查空間:

Segment Name=SYS_LOB0000070763C00004$$
segment_size_blocks     => 1456
segment_size_bytes      => 11927552
used_blocks             => 601
used_bytes              => 4923392
expired_blocks          => 0
expired_bytes           => 0
unexpired_blocks        => 829
unexpired_bytes         => 6791168

您將發現 USED_BYTES 增加,變為大約 5MB 的原始值。

壓縮

SecureFiles 的另一個特性是壓縮。可以使用以下 SQL 壓縮儲存在 LOB 中的值:

SQL> alter table contracts_sec
  2   modify lob(orig_file)
  3  (compress high)
  4  /
 
Table altered.

現在,如果您執行空間查詢 PL/SQL 塊:

Segment Name=SYS_LOB0000070763C00004$$
segment_size_blocks     => 1456
segment_size_bytes      => 11927552
used_blocks             => 201
used_bytes              => 1646592
expired_blocks          => 0
expired_bytes           => 0
unexpired_blocks        => 1229
unexpired_bytes         => 10067968

您將發現 used_bytes 這一度量標準由 5MB 降至現在的 1,646,592 或大約 1.5 MB。

壓縮與重複消除不同。壓縮在每行的 LOB 列內部發生 — 每個 LOB 列單獨壓縮。在重複消除過程中,所有行都受到檢查,列中的重複值將進行刪除並替換為指標。如果您有兩個完全不同的行,重複消除不會減少佔用空間;但是壓縮可以優化 LOB 值的空間。除了對錶執行重複消除過程,您還可以對錶進行壓縮。

由於壓縮佔用 CPU 迴圈,因此如果資料壓縮量不大,可能並不值得進行壓縮。例如,如果您有大量已經壓縮過的 JPEG 照片,則進一步的壓縮不會節省任何空間。但是,如果您有一個作為 CLOB 儲存的 XML 文件,則壓縮可以節省大量空間。SecureFiles 壓縮自動檢測資料是否可壓縮並僅在壓縮可以節省空間時佔用 CPU 迴圈。

Oracle Text 索引可以在壓縮過的 SecureFiles LOB 上安裝。較之檔案系統中的壓縮檔案,這是在 Oracle 資料庫中儲存非結構化資料的主要好處。

此外,請注意,LOB 壓縮與表壓縮無關。壓縮表 CONTRACTS_SEC 並不會壓縮 LOB。LOB 壓縮僅在您執行上述 SQL 時才會發生。

加密

您可以對 SecureFiles 使用透明資料庫加密,就像您將對任何列所做的一樣。下面說明如何使用 AES 128 位加密對列 orig_file LOB 進行加密。

alter table contracts_sec
  modify lob(orig_file)
  (encrypt using 'AES128')
/

啟用加密之前,您需要設定加密錢夾。(加密錢夾的完整描述可以在 Oracle Magazine 文章中找到。)下面是總結的幾個步驟:

  1. 在 sqlnet.ora 中設定引數(如果尚未設定)以指定錢夾的位置:
    ENCRYPTION_WALLET_LOCATION= 
       (SOURCE= 
           (METHOD=FILE) 
           (METHOD_DATA= 
              (DIRECTORY= /opt/oracle/orawall)
         )        
    )       
    
    目錄 /opt/oracle/orawall 應該已經存在;如果不存在,則應該建立該目錄。
  2. 建立錢夾:
    alter system set encryption key authenticated by "mypass"
    
    這將建立口令為 mypass 的錢夾並將其開啟。
  3. 上述步驟只需執行一次。錢夾建立並開啟後,只要資料庫在執行它就會一直開啟(除非人為關閉)。如果資料庫重啟,您需要通過以下語句開啟錢夾:
    alter system set encryption wallet open identified by "mypass"
    

當 SecureFile LOB 列得到加密後,該表所有行的列值都會得到加密。加密後,您不能在表中使用常規匯出或匯入;您需要使用資料泵。

您可以檢視檢視 dba_encrypted_columns 瞭解哪些列已得到加密以及加密的方式。

SQL> select table_name, column_name, encryption_alg
  2  from dba_encrypted_columns
  3  /
 
TABLE_NAME                     COLUMN_NAME        ENCRYPTION_ALG
------------------------------ ------------------ -----------------------------
CONTRACTS_SEC                  ORIG_FILE          AES 128 bits key

快取

較之資料庫駐留物件,在 OS 檔案中儲存非結構化資料的優勢之一是快取工具。檔案可以在作業系統的檔案緩衝區中進行快取。資料庫駐留物件還可以在資料庫緩衝區快取中進行快取。但是,在某些情況下,快取可能竟會損害效能。LOB 通常都很大(該術語大物件就是因此得名),如果它們進入緩衝區快取,大多數其他的資料塊將需要被推送出快取以為要進來的 LOB 騰出空間。該 LOB 可能以後永遠都不會使用,但是它進入緩衝區緩衝卻會導致某些必需的資料塊流出。因此,在大多數情況下,您可能希望對 LOB 禁用快取。

在針對 CONTRACTS_SEC 的示例指令碼中,您使用了 nocache 子句來禁用快取。要為 LOB 啟用快取,您可以對該表進行以下更改:

alter table contracts_sec
modify lob(orig_file)
(cache)
/

這將啟用 LOB 快取。注意該快取只引用 LOB。表的其餘部分放入緩衝區快取,並遵循任何其他表的邏輯(無論該表上的 LOB 快取如何設定)。

快取的優點是非常依賴於應用程式。在處理縮圖的應用程式中,使用快取可能會提高效能。但是,對於大型文件或影像,最好關閉快取。您可以通過 securefiles 進行控制。

日誌記錄

日誌記錄子句決定 LOB 中的資料更改如何記錄到重做日誌流中。與任何其他資料一樣,預設設定為完全日誌記錄,但是由於 LOB 中的資料通常都很大,在某些情況下,您可能希望不進行日誌記錄。上述示例中的 NOLOGING 子句就可以實現該目的。

SecureFiles 為該子句提供了另一個值 filesystem_like_logging,如下所示:

create table contracts_sec_fs
(
        contract_id     number(12),
        contract_name   varchar2(80),
        file_size       number,
        orig_file       blob
)
tablespace users
lob (orig_file)
store as securefile
(
        tablespace users
        enable storage in row
        chunk 4096
        pctversion 20
        nocache
       filesystem_like_logging
)

注意以黑體顯示的行,它將 LOB 後設資料記錄到重做日誌中,而不是記錄整個 LOB。這類似於檔案系統。檔案後設資料記錄到檔案系統日誌中。同樣,SecureFiles 上的該子句會加速崩潰後的恢復。

管理

資料字典檢視 DBA_LOBS 顯示了資料庫中 LOB 的屬性(包括 SecureFiles)。下面是該檢視的列:

列名

說明

OWNER

表的所有者

TABLE_NAME

表的名稱

COLUMN_NAME

LOB 列的名稱

SEGMENT_NAME

LOB 作為單獨的段儲存,由使用者命名,預設為 SYS_LOB…

TABLESPACE_NAME

表空間的名稱

INDEX_NAME

LOB 索引的名稱

CHUNK

LOB 的塊大小

PCTVERSION

在 SecureFiles 中忽略

RETENTION

如果 SecureFile LOB 進行了更新,以前的影像與任何其他資料庫塊一樣儲存在還原段中;但是與資料庫塊不同的是,您可以指定以前的影像儲存多長時間(保留期)。

FREEPOOLS

對 SecureFiles 忽略

CACHE

SecureFile LOB 是否在緩衝池中緩衝(是/否),本文已說明

LOGGING

是否記錄對 SecureFile LOB 進行的更改(是/否),本文已說明

ENCRYPT

SecureFile LOB 是否已加密(是/否),本文已說明

COMPRESSION

SecureFile LOB 是否已壓縮(是/否),本文已說明

DEDUPLICATION

Securefile LOB 是否已進行重複消除(是/否),本文已說明

IN_ROW

LOB 是否按錶行儲存

FORMAT

LOB 是否與平臺的位元組順序有關

PARTITIONED

LOB 是否在分割槽表上

SECUREFILE

LOB 是 SECUREFILE(是或否)還是 BASICFILE

在分割槽表上,LOB 資訊儲存在檢視 DBA_LOB_PARTITIONS 中。

LOB 到 SecureFiles 的移植

既然已經瞭解 SecureFiles 是多麼有用,您可能希望對現有的表進行轉換。最簡單的方法是建立一個新表,載入舊錶中的資料,然後重新命名該表。(當然,這要求這些表在操作期間不可用。)另一種方法是使用 dbms_redefinition 程式包線上重新定義表,不影響可用性。

我們通過一個示例來了解該過程。假設您希望移植原始表 CONTRACTS_BASIC 以儲存為 SecureFiles。要實現該目的,執行以下步驟。

  1. 確保您具有一個主鍵。如果沒有,建立一個。
    alter table contracts_basic
    add constraint pk_contacts
    primary key (contract_id)
    /
    
  2. 構建新表。
    create table contracts_new
    (
    contract_id     number(12),
    contract_name   varchar2(80),
    file_size       number,
    orig_file       BLOB
    )
    lob (orig_file)
    store as securefile
    (nocache nologging)
    /
    
  3. 將列對映到新表。
    declare
      l_col_mapping varchar2(1000);
    begin
      l_col_mapping :=
         'contract_id contract_id , '||
         'contract_name contract_name , '||
         'file_size file_size, '||
         'orig_file orig_file';
         dbms_redefinition.start_redef_table
           ('ARUP', 'CONTRACTS_BASIC', 'CONTRACTS_NEW', l_col_mapping);
    end;
    /
    
  4. 開始重新定義過程。
    declare
       l_error_count pls_integer := 0;
    begin
       dbms_redefinition.copy_table_dependents
       (
          'ARUP', 'CONTRACTS_BASIC', 'CONTRACTS_NEW',
          1, TRUE, TRUE, TRUE, FALSE, l_error_count
       );
       dbms_output.put_line('Errors Occurred := ' || 
           to_char(l_error_count));
    end;
    /
    
    這會將 CONTRACTS_BASIC 中的所有行復制到 CONTRACTS_NEW 中,因此,根據表的行數,該操作可能需要較長時間。
  5. 完成重新定義過程。
    begin  
        dbms_redefinition.finish_redef_table
           ('ARUP', 'CONTRACTS_BASIC', 'CONTRACTS_NEW');
    end;
    /
    
  6. 確認表已得到轉換。
    select securefile
    from dba_lobs
    where table_name = 'CONTRACTS_BASIC'
    /
    
    SEC
    ---
    YES
    
    列顯示 YES,表明列已轉換為 SecureFiles。
  7. 刪除臨時表 CONTRACTS_NEW。
    SQL> drop table contracts_new;
    
    Table dropped.
    

您可以嘗試在開始就啟用並行 DML 以加快複製過程。下面說明如何在會話中啟用並行 DML:

alter session force parallel dml;

初始化引數

初始化引數 db_securefile 決定 SecureFiles 在資料庫中的使用。下面是該引數的各種值及其效果:

效果

PERMITTED

預設值。該值指明可以在資料庫中建立 SecureFile LOB。

ALWAYS

既然您已經知道了 SecureFiles 是多麼有用,您可能希望確保所有 LOB 建立後就應該僅為 SecureFiles 而非預設的 BasicFiles(即使使用者沒有指定 securefile)。該引數值確保所有 LOB 預設情況下建立為 SecureFiles。記住,SecureFiles 需要 ASSM 表空間(在 11g 中為預設設定),因此如果您嘗試在非 ASSM 表空間中建立 LOB,將出現錯誤。

NEVER

與 always 值相反。由於某種原因,您不喜歡 SecureFiles 並且不希望允許它在資料庫中建立。即使使用 SecureFile 子句,該引數值仍然會將 LOB 建立為 BasicFile。當使用了 SecureFile 子句而 LOB 仍然建立為預設的 BasicFile 時,使用者不會收到錯誤訊息。

IGNORE

忽略 securefile 子句以及所有儲存子句。

結論

SecureFiles 不僅是新一代 LOB,它們還為 LOB 帶來了更多的價值,尤其是以前只能在檔案系統領域中獲得的特性。SecureFiles 可以進行加密以確保安全性,可以進行重複消除和壓縮以提高儲存效率,可以進行快取(或不進行快取)以加快訪問(或節省緩衝池空間),可以按多個級別記錄以減少崩潰後的平均恢復時間。引入 SecureFiles 後,您可以在資料庫中儲存更多的非結構化文件,而不會導致過多的開銷,也不會失去 OS 檔案系統提供的任何重要功能。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1384/viewspace-217978/,如需轉載,請註明出處,否則將追究法律責任。

相關文章