Oracle LOB字段使用方法与数据泵迁移实战指南
Oracle数据库中的LOB(Large Object)字段是存储大型非结构化数据的理想选择,支持高达4GB的文本、图像、音频、视频等数据存储。本问简单介绍创建含LOB/CLOB字段
的表结构、批量插入10万行数据、拆分LOB表结构,并提供验证数据完整性的测试用例,适合复习或者学习新知识点用。
一、创建含LOB/CLOB字段的表结构
创建含LOB字段的表时,应根据业务需求选择适当的LOB类型,并合理配置存储参数以优化性能。对于大型文本数据,CLOB是最常用的选择;对于二进制数据,BLOB更为合适。以下是创建含CLOB字段的表的示例,采用SecureFiles存储方式并优化存储参数:
sql
-- 创建专用表空间用于存储LOB数据
sqlplus / as sysdba
CREATE TABLESPACE lob_tbs
DATAFILE '/oradata/ora11g/lob_tbs01.dbf' SIZE 100M AUTOEXTEND ON MAXSIZE UNLIMITED
LOGGING
Extent Management Local
Segment Space Management Auto;
-- 创建含CLOB字段的表,使用SecureFiles存储并优化参数
CREATE TABLE lob_table (
id NUMBER PRIMARY KEY,
text_data CLOB,
binary_data BLOB,
created_at DATE DEFAULT SYSDATE
)
TABLESPACE users
LOB (text_data) STORE AS SECUREFILE (
TABLESPACE lob_tbs
ENABLE STORAGE IN ROW
CHUNK 8192
PCTVERSION 5
COMPRESS HIGH
DEDUPLICATE
NOCACHE
LOGGING
)
LOB (binary_data) STORE AS SECUREFILE (
TABLESPACE lob_tbs
ENABLE STORAGE IN ROW
CHUNK 8192
PCTVERSION 5
COMPRESS HIGH
DEDUPLICATE
NOCACHE
LOGGING
);
关键参数解析:
- SecureFiles:Oracle 11g引入的新存储方式,相比BasicFile提供了更好的性能和更多高级功能
- TABLESPACE lob_tbs:将LOB数据单独存储到专用表空间,避免与常规表数据竞争I/O资源
- ENABLE STORAGE IN ROW:允许小于4KB的LOB值存储在行内,提升小数据访问效率
- CHUNK 8192:设置为8KB(与数据库块大小一致),避免存储碎片
- PCTVERSION 5:将版本空间比例从默认10%降至5%,减少空间浪费
- COMPRESS HIGH:启用高级压缩,可节省约40-60%的存储空间
- DEDUPLICATE:启用去重功能,自动检测并合并重复数据,进一步节省存储空间
- NOCACHE:对于只读或低频更新的LOB数据,禁用缓存可减少内存占用
- LOGGING:启用日志记录,确保数据完整性和可恢复性
二、批量插入10万行测试数据
对于大型LOB数据的批量插入,直接逐行插入效率低下,应采用PL/SQL批量操作方法,结合临时LOB和分批次提交机制,以提高插入效率。
sql
-- 创建存储过程,批量插入10万行数据
CREATE OR REPLACE PROCEDURE insert_lob_data(p_rows IN NUMBER := 100000) IS
-- 定义用于批量插入的集合类型
TYPE t_ids IS TABLE OF NUMBER;
TYPE t_clobs IS TABLE OF CLOB;
v_ids t_ids;
v_clobs t_clobs;
v_buffer VARCHAR2(32767);
v_max_length NUMBER := 5000; -- 每个CLOB数据的平均长度
v_batch_size NUMBER := 1000; -- 每批插入的行数
v_start_time TIMESTAMP := SYSTIMESTAMP;
v_start NUMBER;
v_end NUMBER;
v_current_row NUMBER := 1;
BEGIN
-- 使用 EXECUTE IMMEDIATE 执行 DDL
EXECUTE IMMEDIATE 'ALTER TABLE lob_table NOLOGGING';
DBMS_OUTPUT.PUT_LINE('Generating LOB data and IDs for batches...');
-- 生成随机数据缓冲区(使用英文字符避免乱码)
v_buffer := DBMS_RANDOM.STRING('x', 32767); -- 注意:'x' 表示大小写字母+数字
-- 分批次处理,避免一次性将所有数据加载到内存
FOR batch_num IN 1..CEIL(p_rows / v_batch_size) LOOP
v_start := (batch_num - 1) * v_batch_size + 1;
v_end := LEAST(batch_num * v_batch_size, p_rows);
-- 初始化当前批次的集合
v_ids := t_ids();
v_clobs := t_clobs();
-- 为当前批次生成数据
FOR i IN v_start..v_end LOOP
-- 扩展集合大小
v_ids.EXTEND;
v_clobs.EXTEND;
-- 设置ID
v_ids(v_ids.LAST) := i;
-- 创建并填充临时CLOB
DBMS_LOB.CREATETEMPORARY(v_clobs(v_clobs.LAST), TRUE);
DECLARE
v_written NUMBER := 0;
v_chunk NUMBER := 32767;
v_to_write NUMBER;
v_current_buffer VARCHAR2(32767);
BEGIN
WHILE v_written < v_max_length LOOP
v_current_buffer := SUBSTR(v_buffer, 1, LEAST(32767, v_max_length - v_written));
v_to_write := LENGTH(v_current_buffer);
DBMS_LOB.WRITEAPPEND(v_clobs(v_clobs.LAST), v_to_write, v_current_buffer);
v_written := v_written + v_to_write;
END LOOP;
END;
END LOOP;
DBMS_OUTPUT.PUT_LINE('Inserting batch ' || batch_num || ' (' || v_start || '-' || v_end || ')...');
-- 使用 FORALL 批量插入当前批次的数据
FORALL j IN INDICES OF v_ids
INSERT INTO lob_table (id, text_data)
VALUES (v_ids(j), v_clobs(j));
COMMIT;
DBMS_OUTPUT.PUT_LINE('Batch ' || batch_num || ' committed.');
-- 释放当前批次的临时LOB资源
FOR k IN 1..v_clobs.COUNT LOOP
IF DBMS_LOB.ISTEMPORARY(v_clobs(k)) = 1 THEN
DBMS_LOB.FREETEMPORARY(v_clobs(k));
END IF;
END LOOP;
END LOOP;
-- 插入完成后恢复 LOGGING 模式
EXECUTE IMMEDIATE 'ALTER TABLE lob_table LOGGING';
-- 输出统计信息
DBMS_OUTPUT.PUT_LINE('Total rows inserted: ' || p_rows);
DBMS_OUTPUT.PUT_LINE('Time taken: ' || TO_CHAR(SYSTIMESTAMP - v_start_time));
EXCEPTION
WHEN OTHERS THEN
-- 异常处理:尝试释放已创建的临时LOB
IF v_clobs IS NOT NULL THEN
FOR k IN 1..v_clobs.COUNT LOOP
IF v_clobs.EXISTS(k) AND DBMS_LOB.ISTEMPORARY(v_clobs(k)) = 1 THEN
DBMS_LOB.FREETEMPORARY(v_clobs(k));
END IF;
END LOOP;
END IF;
-- 回滚事务
ROLLBACK;
-- 重新抛出异常
RAISE;
END;
/
select bytes/1024/1024 size_mb from dba_segments where segment_name = upper('lob_table');
SIZE_MB
----------
.0625
批量插入优化策略:
- 临时LOB :使用
DBMS_LOB.Createtemporary
创建临时LOB对象,减少磁盘I/O - 绑定变量:避免在SQL语句中直接拼接LOB值,使用绑定变量提高效率
- 分批次提交:每1000行提交一次事务,平衡锁表时间和日志管理
- NOLOGGING模式:在批量插入前启用NOLOGGING模式,可提高插入速度约3-5倍
- 并行执行 :对于超大规模数据,可考虑使用
PARALLEL
提示并行插入
执行上述存储过程插入10万行数据:
sql
SET SERVEROUTPUT ON;
BEGIN
insert_lob_data(100000); -- 插入10万行
END;
/
-- 插入数据过程中,表一直在变大
select bytes/1024/1024 size_mb from dba_segments where segment_name = upper('lob_table');
SIZE_MB
----------
663
SQL> /
SIZE_MB
----------
671
插入性能优化:
对于超大规模数据插入,建议采用以下优化措施:
- 使用
FORALL
批量插入代替循环逐行插入 - 启用
COMMIT
前的NOLOGGING
模式,减少重做日志开销 - 调整
PCTVERSION
参数,减少版本空间占用 - 考虑使用外部表或SQL*Loader进行超大规模数据导入
三、拆分LOB表结构
为提高大型LOB表的查询性能和管理便利性,可采用表空间分离或分区表策略拆分LOB数据。
表空间分离是最简单有效的方式,将LOB数据存储到独立表空间,避免与常规表数据竞争I/O资源。
sql
-- 创建专用表空间用于存储拆分后的LOB数据
CREATE TABLESPACE lob_tbs_split
DATAFILE '/oradata/ora11g/lob_tbs_split01.dbf' SIZE 100M AUTOEXTEND ON MAXSIZE UNLIMITED
LOGGING
Extent Management Local
Segment Space Management Auto;
-- 将LOB数据移动到新表空间
ALTER TABLE lob_table MOVE LOB (text_data) STORE AS (TABLESPACE lob_tbs_split);
ALTER TABLE lob_table MOVE LOB (binary_data) STORE AS (TABLESPACE lob_tbs_split);
这些命令只是移动了LOB列的存储段(LOB Segment)到 lob_tbs_split 表空间,但并没有移动表本身的数据段(Table Segment)。
因此:
表的数据段(存储 id, created_at 等非LOB列)仍然在 USERS 表空间
LOB的数据段(存储 text_data, binary_data 列)已经移动到 lob_tbs_split 表空间
set lin 100
col SEGMENT_NAME for a30
col TABLESPACE_NAME for a30
SELECT
SEGMENT_NAME,
SEGMENT_TYPE,
TABLESPACE_NAME
FROM DBA_SEGMENTS
WHERE SEGMENT_NAME IN (
SELECT SEGMENT_NAME
FROM USER_LOBS
WHERE TABLE_NAME = 'LOB_TABLE'
);
SEGMENT_NAME SEGMENT_TYPE TABLESPACE_NAME
------------------------------ ------------------------------------ ------------------------------
SYS_LOB0000112369C00002$$ LOBSEGMENT LOB_TBS_SPLIT
SYS_LOB0000112369C00003$$ LOBSEGMENT LOB_TBS_SPLIT