Oracle 數據庫 11g面向 DBA 和開發人員的重要新特性:SecureFiles:
1、SecureFiles:新 LOB
瞭解如何使用新一代 LOB:SeureFiles。SecureFiles 集外部文件和數據庫 LOB 方法的優點於一身,可以存儲非結構化數據,允許加密、壓縮、重複消除等等。
數據庫駐留 BLOBS 或 OS 文件
您在 Oracle 數據庫中存儲什麼?通常,您會以關係格式存儲數據以便於映射到某些定義模式的類型,或者以客戶姓名、帳戶餘額、狀態代碼等定義的數據類型存儲數據。但是,以非結構化或半結構化格式存儲信息的需求也日益增加。例如,照片、字處理文檔、電子表格、XML 文件等等。這些類型的數據如何存儲?
通常有兩種方法:這些數據作爲 LOB 字段(BLOB 用於存儲二進制數據,CLOB 用於存儲字符數據)存儲在數據庫中,或者通過引用存儲在數據庫中的文件存儲在 OS 文件中。
每種方法都各具優缺點。OS 文件可以由 OS 和日誌文件系統緩存以加速崩潰後的恢復。由於可進行壓縮,因此 OS 文件佔用的空間通常也比數據庫中的數據要少。
還有一些工具可以智能地識別文件模式並消除重複從而提高存儲效率;但是 OS 文件位於數據庫外部,因此數據庫屬性不適用於它們。這些文件不進行備份、細粒度安全性不適用於它們,此類文件不是事務的一部分 — 因此 Oracle 數據庫固有的讀取一致性等該概念不適用於它們。
如果能集兩種方法的優點於一身會怎樣?Oracle 數據庫 11g 中的 SecureFiles 爲您提供了答案。SecureFiles 是數據庫中一個全新的基礎架構,可提供最佳的數據庫駐留 LOB 和 OS 文件性能。讓我們看一看這種方案的實現方式。(順便說明的是,傳統 LOB 仍然以 BasicFiles 格式提供。)
2、實際示例
通過一個簡單的示例來介紹 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 後,您就可以獲得很多能優化操作的特性。下面就是其中一些很有用的特性。
3、重複消除
重複消除可能是 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 的原始值。
4、壓縮
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 時纔會發生。
5、加密
您可以對 SecureFiles 使用透明數據庫加密,就像您將對任何列所做的一樣。下面說明如何使用 AES 128 位加密對列 orig_file LOB 進行加密。
alter table contracts_sec
modify lob(orig_file)
(encrypt using 'AES128')
/
啓用加密之前,您需要設置加密錢夾。(加密錢夾的完整描述可以在此 Oracle Magazine 文章中找到。)下面是總結的幾個步驟:
在 sqlnet.ora 中設置參數(如果尚未設置)以指定錢夾的位置:
ENCRYPTION_WALLET_LOCATION=
(SOURCE=
(METHOD=FILE)
(METHOD_DATA=
(DIRECTORY= /opt/oracle/orawall)
)
)
目錄 /opt/oracle/orawall 應該已經存在;如果不存在,則應該創建該目錄。
創建錢夾:
alter system set encryption key authenticated by "mypass"
這將創建口令爲 mypass 的錢夾並將其打開。
上述步驟只需執行一次。錢夾創建並打開後,只要數據庫在運行它就會一直打開(除非人爲關閉)。如果數據庫重啓,您需要通過以下語句打開錢夾:
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
6、緩存
較之數據庫駐留對象,在 OS 文件中存儲非結構化數據的優勢之一是緩存工具。文件可以在操作系統的文件緩衝區中進行緩存。數據庫駐留對象還可以在數據庫緩衝區緩存中進行緩存。但是,在某些情況下,緩存可能竟會損害性能。LOB 通常都很大(該術語大對象就是因此得名),如果它們進入緩衝區緩存,大多數其他的數據塊將需要被推送出緩存以爲要進來的 LOB 騰出空間。該 LOB 可能以後永遠都不會使用,但是它進入緩衝區緩衝卻會導致某些必需的數據塊流出。因此,在大多數情況下,您可能希望對 LOB 禁用緩存。
在針對 CONTRACTS_SEC 的示例腳本中,您使用了 nocache 子句來禁用緩存。要爲 LOB 啓用緩存,您可以對該表進行以下更改:
alter table contracts_sec
modify lob(orig_file)
(cache)
/
這將啓用 LOB 緩存。注意該緩存只引用 LOB。表的其餘部分放入緩衝區緩存,並遵循任何其他表的邏輯(無論該表上的 LOB 緩存如何設置)。
緩存的優點是非常依賴於應用程序。在處理縮略圖的應用程序中,使用緩存可能會提高性能。但是,對於大型文檔或圖像,最好關閉緩存。您可以通過 securefiles 進行控制。
7、日誌記錄
日誌記錄子句決定 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 中。
8、LOB 到 SecureFiles 的移植
既然已經瞭解 SecureFiles 是多麼有用,您可能希望對現有的表進行轉換。最簡單的方法是創建一個新表,載入舊錶中的數據,然後重命名該表。(當然,這要求這些表在操作期間不可用。)另一種方法是使用 dbms_redefinition 程序包在線重新定義表,不影響可用性。
我們通過一個示例來了解該過程。假設您希望移植原始表 CONTRACTS_BASIC 以存儲爲 SecureFiles。要實現該目的,執行以下步驟。
確保您具有一個主鍵。如果沒有,創建一個。
alter table contracts_basic
add constraint pk_contacts
primary key (contract_id)
/
構建新表。
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)
/
將列映射到新表。
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;
/
開始重新定義過程。
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 中,因此,根據表的行數,該操作可能需要較長時間。
完成重新定義過程。
begin
dbms_redefinition.finish_redef_table
('ARUP', 'CONTRACTS_BASIC', 'CONTRACTS_NEW');
end;
/
確認表已得到轉換。
select securefile
from dba_lobs
where table_name = 'CONTRACTS_BASIC'
/
SEC
---
YES
列顯示 YES,表明列已轉換爲 SecureFiles。
刪除臨時表 CONTRACTS_NEW。
SQL> drop table contracts_new;
Table dropped.
您可以嘗試在開始就啓用並行 DML 以加快複製過程。下面說明如何在會話中啓用並行 DML:
alter session force parallel dml;
9、初始化參數
初始化參數 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 文件系統提供的任何重要功能。 新 LOB