SecureFile功能是oracle 11g中对大对象(LOB)存储格式的完全重新设计实现,原来的LOB存储格式现在通称为BASICFILE,它仍然是默认的存储方法,但是SECURFILE关键字开启了新的存储方法,它允许加密、利用压缩节约空间和数据重复消除。
初始化参数
SecureFile功能在初始化参数COMPATIBLE设置为11.0.0.0.0或更高时可用。
DB_SECUREFILE初始化参数控制数据库对LOB存储格式的默认行为,允许的值有:
◆ALWAYS - 在ASSM表空间中的所有LOB对象以SecureFile LOB的格式创建,在非ASSM表空间中的所有LOB对象以BasicFile LOB的格式创建(除非明确地指出要以SecureFile格式创建),在没有指定选项的情况下,BasicFile存储格式选项被忽略,SecureFile默认存储格式选项被使用。
◆FORCE - 所有LOB对象都以SecureFile LOB格式创建,如果是在一个非ASSM表空间中创建LOB,会出现错误,在没有指定选项的情况下,BasicFile存储格式选项被忽略,SecureFile默认存储格式选项被使用。
◆PERMITTED - 默认设置,当使用了SECUREFILE关键字时它允许SecureFile LOB存储格式,默认存储方法是BASICFILE。
◆NEVER - 不允许创建SecureFile LOB对象。
◆IGNORE - 防止创建SecureFile LOB,使用SecureFile存储选项时忽略所有错误。
这个参数是动态的,因此它可以使用ALTER SYSTEM命令设置。
SQL> ALTER SYSTEM SET db_securefile = 'FORCE';
System altered.
SQL> ALTER SYSTEM SET db_securefile = 'PERMITTED';
System altered.
SQL>
下面的例子假设DB_SECUREFILE初始化参数设置为默认值PERMITTED。
创建SecureFile LOB
基础
SecureFile LOB通过在LOB存储子句后添加SECUREFILE关键字来创建,下面的代码显示创建了两个表,第一个使用的是原来的存储格式,第二个使用的是SecureFile存储格式。
CREATE TABLE bf_tab (
id NUMBER,
clob_data CLOB
)
LOB(clob_data) STORE AS BASICFILE;
INSERT INTO bf_tab VALUES (1, 'My CLOB data');
COMMIT;
CREATE TABLE sf_tab (
id NUMBER,
clob_data CLOB
)
LOB(clob_data) STORE AS SECUREFILE;
INSERT INTO sf_tab VALUES (1, 'My CLOB data');
COMMIT;
LOB重复消除
SecureFile的DEDUPLICATE选项允许在表或分区一级上的一个LOB内消除重复数据,正如你预料的那样,这个技术与预防重写导致系统开销增大,KEEP_DUPLICATE选项明确地阻止重复消除,下面的例子对比了普通的SecureFile和重复消除SecureFile的空间使用情况。
CREATE TABLE keep_duplicates_tab (
id NUMBER,
clob_data CLOB
)
LOB(clob_data) STORE AS SECUREFILE keepdup_lob(
KEEP_DUPLICATES
);
CREATE TABLE deduplicate_tab (
id NUMBER,
clob_data CLOB
)
LOB(clob_data) STORE AS SECUREFILE dedup_lob (
DEDUPLICATE
);
DECLARE
l_clob CLOB := RPAD('X', 10000, 'X');
BEGIN
FOR i IN 1 .. 1000 LOOP
INSERT INTO keep_duplicates_tab VALUES (i, l_clob);
END LOOP;
COMMIT;
FOR i IN 1 .. 1000 LOOP
INSERT INTO deduplicate_tab VALUES (i, l_clob);
END LOOP;
COMMIT;
END;
/
EXEC DBMS_STATS.gather_table_stats(USER, 'keep_duplicates_tab');
EXEC DBMS_STATS.gather_table_stats(USER, 'deduplicate_tab');
COLUMN segment_name FORMAT A30
SELECT segment_name, bytes
FROM user_segments
WHERE segment_name IN ('KEEPDUP_LOB', 'DEDUP_LOB');
SEGMENT_NAME BYTES
------------------------------ ----------
DEDUP_LOB 262144
KEEPDUP_LOB 19267584
2 rows selected.
SQL>
注意重复消除段要小很多,空间节约依赖于LOB段内的重复程度,重复模式可以使用ALTER TABLE命令进行重新设置。
ALTER TABLE deduplicate_tab MODIFY LOB(clob_data) (
KEEP_DUPLICATES
);
EXEC DBMS_STATS.gather_table_stats(USER, 'deduplicate_tab');
COLUMN segment_name FORMAT A30
SELECT segment_name, bytes
FROM user_segments
WHERE segment_name IN ('KEEPDUP_LOB', 'DEDUP_LOB');
SEGMENT_NAME BYTES
------------------------------ ----------
DEDUP_LOB 76808192
KEEPDUP_LOB 68091904
2 rows selected.
SQL>
LOB压缩
SecureFile的COMPRESS选项在表或分区一级上开启了对LOB内容的压缩,使用关键字MEDIUM和HIGH表示压缩的等级,如果没有指定压缩等级,就默认为MEDIUM,对LOB内容进行压缩会增加系统开销,因此使用高等级的压缩可能会对系统性能产生不良影响,SecureFile LOB的压缩功能不影响表压缩,反之亦然,下面的例子对比了使用普通的SecureFile LOB和启用压缩功能的SecureFile LOB的空间利用情况。
CREATE TABLE nocompress_tab (
id NUMBER,
clob_data CLOB
)
LOB(clob_data) STORE AS SECUREFILE nocompress_lob(
NOCOMPRESS
);
CREATE TABLE compress_tab (
id NUMBER,
clob_data CLOB
)
LOB(clob_data) STORE AS SECUREFILE compress_lob (
COMPRESS HIGH
);
DECLARE
l_clob CLOB := RPAD('X', 10000, 'X');
BEGIN
FOR i IN 1 .. 1000 LOOP
INSERT INTO nocompress_tab VALUES (i, l_clob);
END LOOP;
COMMIT;
FOR i IN 1 .. 1000 LOOP
INSERT INTO compress_tab VALUES (i, l_clob);
END LOOP;
COMMIT;
END;
/
EXEC DBMS_STATS.gather_table_stats(USER, 'nocompress_tab');
EXEC DBMS_STATS.gather_table_stats(USER, 'compress_tab');
COLUMN segment_name FORMAT A30
SELECT segment_name, bytes
FROM user_segments
WHERE segment_name IN ('COMPRESS_LOB', 'NOCOMPRESS_LOB');
SEGMENT_NAME BYTES
------------------------------ ----------
COMPRESS_LOB 131072
NOCOMPRESS_LOB 71565312
2 rows selected.
SQL>
我们可以看到压缩的LOB段比没有压缩的LOB段明显要小得多,空间节约的程度依赖于LOB段数据的存储类型。
可以使用ALTER TABLE命令重新设置压缩模式。
ALTER TABLE compress_tab MODIFY LOB(clob_data) (
NOCOMPRESS
);
EXEC DBMS_STATS.gather_table_stats(USER, 'compress_tab');
COLUMN segment_name FORMAT A30
SELECT segment_name, bytes
FROM user_segments
WHERE segment_name IN ('COMPRESS_LOB', 'NOCOMPRESS_LOB');
SEGMENT_NAME BYTES
------------------------------ ----------
COMPRESS_LOB 76808192
NOCOMPRESS_LOB 71630848
2 rows selected.
SQL>
LOB加密
SecureFile LOB的加密功能依赖于钱夹或硬件安全模型(HSM)掌管加密密钥,钱夹设置与透明数据加密(TDE)和表空间加密描述的一样,因此在尝试下面的例子前先完成那两个实验。
SecureFile的ENCRYPT选项执行块级别的LOB内容加密,一个可选的USING子句定义了使用哪种加密算法(3DES168, AES128, AES192, 或AES256),默认使用AES192算法,NO SALT选项对于SecureFile加密不可用,加密是应用在每一列上的,因此它会影响所有使用LOB的分区,DECRPT选项用于明确地阻止加密,下面的例子显示了使用加密的SecureFile LOB数据类型创建表。
CREATE TABLE encrypt_tab (
id NUMBER,
clob_data CLOB
)
LOB(clob_data) STORE AS SECUREFILE encrypt_lob(
ENCRYPT USING 'AES256'
);
ALTER TABLE命令可以用于加密或解密现有的列,要切换加密算法你必须使用REKEY选项。
ALTER TABLE encrypt_tab MODIFY (
clob_data CLOB DECRYPT
);
ALTER TABLE encrypt_tab MODIFY (
clob_data CLOB ENCRYPT USING '3DES168'
);
ALTER TABLE encrypt_tab REKEY USING 'AES192';
加密是不受原来的导入导出工具或表空间传输支持的,因此必须使用数据泵导入导出工具来传输数据。
LOB缓存和日志
BasicFile和SecureFile LOB共享了部分基础的缓存和日志选项,常见的缓存选项有:
◆CACHE - LOB数据被放在缓冲区中。
◆CACHE READES - 仅读取LOB数据过程中它放在缓冲区中,写操作时不放进去。
◆NOCACHE - LOB数据不放在缓冲区中,这是BasicFile和SecureFile LOB的默认值。
基本的日志选项有:
◆LOGGING - 创建和修改LOB时产生完全重做日志,这是默认设置。
◆NOLOGGING - 操作不记录在重做日志中,因此不能恢复,在首次创建和巨大的载入过程中有用。
而且,SecureFile LOB还有一个日志选项FILESYSTEM_LIKE_LOGGING,只记录元数据,在出现故障后仍然允许段的恢复。
CACHE选项意味着LOGGING,因此你不能将CACHE与NOLOGGING或FILESYSTEM_LIKE_LOGGING合在一起使用。
下面的代码显示了一个在表创建过程中和创建后明确设置了缓存和日志选项的例子。
CREATE TABLE caching_and_logging_tab (
id NUMBER,
clob_data CLOB
)
LOB(clob_data) STORE AS SECUREFILE(
NOCACHE
FILESYSTEM_LIKE_LOGGING
);
ALTER TABLE caching_and_logging_tab MODIFY LOB(clob_data) (
CACHE
);
PL/SQL API
DBMS_LOB包用于访问BasicFile和SecureFile LOB,SETOPTIONS存储过程和GETOPTIONS函数允许在每个LOB列上设置压缩、加密和重复消除选项。
CREATE TABLE securefile_tab (
id NUMBER,
clob_data CLOB
)
LOB(clob_data) STORE AS SECUREFILE securefile_lob(
encrypt
compress
);
INSERT INTO securefile_tab VALUES (1, 'ONE');
INSERT INTO securefile_tab VALUES (2, 'TWO');
COMMIT;
SET SERVEROUTPUT ON
DECLARE
l_clob CLOB;
BEGIN
SELECT clob_data
INTO l_clob
FROM securefile_tab
WHERE id = 1
FOR UPDATE;
DBMS_OUTPUT.put_line('Compression : ' || DBMS_LOB.getoptions(l_clob, DBMS_LOB.opt_compress));
DBMS_OUTPUT.put_line('Encryption : ' || DBMS_LOB.getoptions(l_clob, DBMS_LOB.opt_encrypt));
DBMS_OUTPUT.put_line('Deduplication: ' || DBMS_LOB.getoptions(l_clob, DBMS_LOB.opt_deduplicate));
ROLLBACK;
END;
/
DBMS_SPACE包括SPACE_USAGE存储过程,它返回有关所有LOB段中的LOB磁盘空间的消耗情况,这个存储过程也可以用于ASSM表空间。
SET SERVEROUTPUT ON
DECLARE
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
DBMS_SPACE.SPACE_USAGE(
segment_owner => 'TEST',
segment_name => 'SECUREFILE_LOB',
segment_type => 'LOB',
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;
/
迁移到SecureFile
目前还没有方法可以自动将一个BasicFile列迁移到SecureFile LOB列,相反,你必须使用下面的方法手动转换数据:
◆CREATE TABLE ... AS SELECT ...
◆INSERT INTO ... SELECT ...
◆表在线重定义
◆导出/导入
◆创建一个新列,用原始列的值更新新列,然后删除旧列
◆创建一个新列,用原始列的值更新新列,重命名表,用原始表名创建一个仅引用新列的视图
除导出/导入方法外,在转换包含有大量数据的LOB时,所有选项都需要注意磁盘空间的数量。
oralce流目前不支持SecureFile,因此要避免迁移使用了流的LOB对象。