oracle 19c中lob引发的i/o等待本质是隐性高负载,主因是basicfile lob在assm表空间下未启用行移动、压缩及securefiles,导致碎片化与块分裂;须通过dba_lobs和dba_segments确认实际存储类型,并用dba_hist_seg_stat定位写入热点,迁移需重建列而非alter modify。
lob字段引发的i/o等待,在oracle 19c中往往不是“慢”,而是“隐性高负载”——db time不突出,但physical reads和physical writes持续偏高,enq: hw - contention或db file sequential read在top 5等待事件里反复出现。根本原因常是basicfile lob在assm表空间下未启用行移动、无压缩、无securefiles特性,导致写入碎片化严重、块分裂频繁、归档日志暴增。
查LOB段是否还在用BASICFILE
别只看表定义,得确认实际存储类型。BASICFILE在19c仍可创建,但已不推荐,且无法享受压缩/加密/去重:
-
SELECT table_name, column_name, segment_name, securefile FROM dba_lobs WHERE owner = 'YOUR_SCHEMA' AND table_name = 'YOUR_TABLE';—— 若securefile列返回NO,就是BASICFILE - 配合
dba_segments确认段物理属性:SELECT segment_name, segment_type, bytes/1024/1024 AS mb FROM dba_segments WHERE segment_name IN (SELECT segment_name FROM dba_lobs WHERE owner = 'YOUR_SCHEMA');看LOB段是否异常庞大(比如单个LOB段超50GB却读写频次不高) - 注意:即使建表时写了
SECUREFILE,若db_securefile参数为NEVER或表空间非ASSM,仍会退化为BASICFILE
AWR里定位LOB写入热点
光看Load Profile里的Physical Writes总量没用,要钻到段级。关键视图是DBA_HIST_SEG_STAT,它记录每小时快照中各段的物理写次数(physical_writes)和逻辑写(logical_writes):
- 执行:
SELECT snap_id, object_name, subobject_name, physical_writes, logical_writes FROM dba_hist_seg_stat WHERE object_name IN (SELECT table_name FROM dba_lobs WHERE owner = 'YOUR_SCHEMA') AND snap_id BETWEEN &start_snap AND &end_snap ORDER BY physical_writes DESC; - 重点对比
physical_writes / logical_writes比值:若>0.8,说明大量写操作未命中buffer cache,直写磁盘,极可能是LOB未启用缓存(NOLOGGING或CACHE未设)、或频繁UPDATE导致重写整个LOB块 - 若
subobject_name为空,说明是主LOB段;若有值(如SYS_LOBxxx),说明是索引LOB段——后者高写入通常意味着LOB索引分裂严重,需重建
改BASICFILE为SECUREFILE不能ALTER COLUMN直接改
这是最容易踩的坑:19c不支持ALTER TABLE ... MODIFY LOB (...) (SECUREFILE)。必须重建LOB列,且过程需规避锁表和归档风暴:
- 先确认表空间是ASSM:
SELECT tablespace_name, segment_space_management FROM dba_tablespaces WHERE tablespace_name = 'YOUR_LOB_TBS';必须为AUTO - 临时新增SECUREFILE列:
ALTER TABLE your_table ADD (lob_col_new CLOB SECUREFILE COMPRESS MEDIUM DEDUPLICATE); - 分批迁移(避免长事务):
UPDATE your_table SET lob_col_new = lob_col WHERE rowid IN (SELECT rowid FROM your_table WHERE rownum ,提交后循环 - 最后
RENAME列并DROP旧列。注意:原BASICFILE列的索引、约束、触发器需手动迁移 - 切勿用
CREATE TABLE AS SELECT全量重建——LOB数据可能达TB级,中间表+重命名会卡死DDL,且丢失统计信息和依赖对象
LOB写入优化还绕不开db_securefile参数
这个参数控制全局LOB行为,动态修改后仅对新创建的LOB生效,已有LOB不受影响:
-
SHOW PARAMETER db_securefile查当前值。生产库建议设为PREFERRED(优先SecureFile,退化时才BasicFile)或ALWAYS(强制,但要求表空间必须ASSM) - 动态修改:
ALTER SYSTEM SET db_securefile = PREFERRED SCOPE=BOTH; - 如果看到
WRH$_SEG_STAT里LOB段的physical_writes在夜间批量更新后飙升,大概率是ETL任务用了INSERT /*+ APPEND */但LOB列没设NOCACHE,导致Direct Path Insert仍走buffer cache写入——此时应显式加NOCACHE并确保db_securefile允许SecureFile
真正难处理的不是怎么改,而是改完之后没人验证效果。LOB优化后必须对比同一业务窗口的两份AWR报告,重点盯Physical Writes Per Sec下降比例、HW contention等待是否消失、以及DB Time中DB CPU占比是否上升(说明I/O瓶颈解除,CPU开始成为新瓶颈)。否则你只是把问题从磁盘搬到了CPU上。











