Oracle Database 11g: 作者:Arup Nanda |
了解如何使用新一代 LOB:SecureFiles。SecureFiles 集外部文件与数据库 LOB 方法的优点于一身,可以存储非结构化数据,允许加密、压缩、重复消除等。
您在 Oracle 数据库中存储什么?通常,您会以关系格式存储数据以便映射到某些定义模式的类型,或者以客户姓名、帐户余额、状态代码等定义的数据类型存储数据。但是,以非结构化或半结构化形式存储信息的需求也日益增加。例如,照片、字处理文档、电子表格、XML 文件等等。这些类型的数据如何存储?
通常有两种方法:这些数据作为 LOB 字段(BLOB 用于存储二进制数据,CLOB 用于存储字符数据)存储在数据库中,或者通过引用存储在数据库中的文件存储在操作系统文件中。
每种方法都有优缺点。操作系统文件可以由操作系统和日志文件系统缓存以加速崩溃后的恢复。由于可进行压缩,因此操作系统文件占用的空间通常也比数据库中的数据要少。
还有一些工具可以智能地识别文件模式并消除重复从而提高存储效率;但是操作系统文件位于数据库外部,因此数据库属性不适用于它们。这些文件不进行备份,细粒度安全性不适用于它们,此类文件不是事务的一部分 — 因此 Oracle 数据库固有的读取一致性等概念不适用于它们。
如果能集两种方法的优点于一身会怎样?Oracle Database 11g 中的 SecureFiles 为您提供了答案。SecureFiles 是数据库中一个全新的基础架构,可提供最佳的数据库驻留 LOB 和操作系统文件性能。让我们看一看这种方案的实现方式。(顺便说明的是,传统 LOB 仍然以 BasicFiles 格式提供。)
通过一个简单的示例来介绍 SecureFiles 概念或许是一种最佳方式。假设您要开发一个合同管理系统,在该系统中您希望将所有合同的副本都放在一个表中。扫描的文档通常是 PDF 文件而非文本。某些可能是 MS Word 文档,甚至是扫描的照片。这是 BLOB 的最佳使用案例,因为列必需能够支持二进制数据。
过去,在 Oracle Database 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 ); \
如果您希望将 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)要创建 SecureFile LOB,您需要满足两个条件,而这两个条件都是默认设置,因此您可能已经满足。
store as securefile ( tablespace users enable storage in row chunk 4096 pctversion 20 nocache nologging ) /
表创建完成后,您可以加载数据,方式与 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; /
SQL> create directory secfile as ’/opt/oracle’;
重复消除可能是 SecureFiles 中最受欢迎的特性,它由于操作系统文件在某些高端文件系统中相对于数据库驻留 BLOB 的优势而备受推崇。假设一个表有五条记录,每条记录有一个 BLOB。其中三个 BLOB 是完全相同的。如果能够只存储该 BLOB 一次而在其他两个记录上只存储对该副本的引用将极大地减少空间消耗。这在操作系统文件中是可行的,但在 Oracle Database 10g LOB 中则无法实现。但是如果使用 SecureFiles,只需通过一个称为重复消除的属性即可轻松搞定。可以在表创建时指定该属性,也可以在以后将其修改为:
SQL> alter table contracts_sec 2 modify lob(orig_file) 3 (deduplicate) 4 / Table altered.
下面,我们来看看进行重复消除过程后的空间节省情况。可以通过程序包 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; /
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
您也可以反向进行重复消除过程:
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
SecureFiles 的另一个特性是压缩。可以使用以下 SQL 压缩 LOB 中存储的值:
SQL> alter table contracts_sec 2 modify lob(orig_file) 3 (compress high) 4 / Table altered.
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
压缩与重复消除不同。压缩在每行的 LOB 列内部发生 — 每个 LOB 列单独压缩。在重复消除过程中,所有行都受到检查,列中的重复值将进行删除并替换为指针。如果您有两个完全不同的行,重复消除不会减少占用空间;但是压缩可以优化 LOB 值的空间。除了对表执行重复消除过程,您还可以对表进行压缩。
由于压缩占用 CPU 周期,因此如果数据压缩量不大,可能并不值得进行压缩。例如,如果您有大量已经压缩过的 JPEG 照片,那么进一步的压缩不会节省任何空间。但是,如果您有一个作为 CLOB 存储的 XML 文档,那么压缩可以节省大量空间。SecureFiles 压缩自动检测数据是否可压缩并仅在压缩可以节省空间时占用 CPU 周期。
Oracle Text 索引可以在压缩过的 SecureFiles LOB 上安装。较之文件系统中的压缩文件,这是在 Oracle 数据库中存储非结构化数据的主要好处。
此外,请注意,LOB 压缩与表压缩无关。压缩表 CONTRACTS_SEC 并不会压缩 LOB。LOB 压缩仅在您执行上述 SQL 时才会发生。
在 Oracle Database 11g 第 2 版中,除了 HIGH 和 MEDIUM 两个选项外,还提供了第三个压缩选项:LOW。顾名思义,该选项的压缩率比较低,但占用的 CPU 也要少得多,能够更快速地完成压缩。这种方法使用类似于快速 Lempel–Ziv–Oberhumer (LZO) 算法的基于块的无损压缩。
我们来看一个使用 SecureFiles LOW 压缩的表的示例:
create table docs ( doc_id number, clearance varchar2(20), doc_body clob ) LOB(doc_body) store as securefile ( compress low ) /
我们来看一个使用相同表和列的示例。首先,我们将该列修改为未压缩:
SQL> alter table docs 2 modify LOB(doc_body) 3 ( 4 nocompress 5 ) 6 / Table altered.
SQL> alter table docs 2 modify LOB(doc_body) 3 ( 4 compress low 5 ) 6 / Table altered.
您可以对 SecureFiles 使用透明数据库加密,就像您将对任何列所做的一样。下面说明如何使用 AES 128 位加密对列 orig_file LOB 进行加密。
alter table contracts_sec modify lob(orig_file) (encrypt using 'AES128') /
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"
您可以查看视图 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
较之数据库驻留对象,在操作系统文件中存储非结构化数据的优势之一是缓存工具。文件可以在操作系统的文件缓冲区中进行缓存。数据库驻留对象还可以在数据库缓冲区缓存中进行缓存。但是,在某些情况下,缓存可能会损害性能。LOB 通常都很大(该术语大对象就是因此得名),如果它们进入缓冲区缓存,大多数其他的数据块将需要被推送出缓存以便为要进来的 LOB 腾出空间。该 LOB 可能以后永远都不会使用,但是它进入缓冲区缓冲却会导致某些必需的数据块被赶出去。因此,在大多数情况下,您可能希望对 LOB 禁用缓存。
在针对 CONTRACTS_SEC 的示例脚本中,您使用了 nocache 子句来禁用缓存。要为 LOB 启用缓存,您可以对该表进行以下更改:
alter table contracts_sec modify lob(orig_file) (cache) /
缓存的优点是非常依赖于应用程序。在处理缩略图的应用程序中,使用缓存可能会提高性能。但是,对于大型文档或图像,最好关闭缓存。您可以通过 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注意以黑体显示的行,它将 LOB 元数据记录到重做日志中,而不是记录整个 LOB。这类似于文件系统。文件元数据记录到文件系统日志中。同样,SecureFiles 上的该子句会加速崩溃后的恢复。
filesystem_like_logging )
数据字典视图 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 是否在缓冲池中缓冲 (Yes/No),本文已说明 |
LOGGING | 是否记录对 SecureFile LOB 进行的更改 (Yes/No),本文已说明 |
ENCRYPT | SecureFile LOB 是否已加密 (Yes/No),本文已说明 |
COMPRESSION | SecureFile LOB 是否已压缩 (Yes/No),本文已说明 |
DEDUPLICATION | Securefile LOB 是否已进行重复消除 (Yes/No),本文已说明 |
IN_ROW | LOB 是否按表行存储 |
FORMAT | LOB 是否与平台的字节顺序有关 |
PARTITIONED | LOB 是否在分区表上 |
SECUREFILE | LOB 是 SECUREFILE (Yes/No) 还是 BASICFILE |
在分区表上,LOB 信息存储在视图 DBA_LOB_PARTITIONS 中。
既然已经了解 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; /
begin dbms_redefinition.finish_redef_table ('ARUP', 'CONTRACTS_BASIC', 'CONTRACTS_NEW'); end; /
select securefile from dba_lobs where table_name = 'CONTRACTS_BASIC' / SEC --- YES
SQL> drop table contracts_new; Table dropped.
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 文件系统提供的任何重要功能。