oracle 12c 中因大量并发写入引发的段扩展性能争用,核心是多个会话同时触发同一段(segment)的高水位线(hwm)推进,产生 enq: hw - contention 等待。这不是锁表问题,也不靠调大 sga 或 undo 就能缓解——关键在减少 hwm 移动频次、分散空间分配压力。
怎么快速定位正在争抢 HWM 的具体段?
不能只看等待事件,必须从 v$session_wait 的 P3 值反查物理块位置,再映射到真实对象:
-
P3是相对数据块地址(RDBA),用DBMS_UTILITY.data_block_address_file和DBMS_UTILITY.data_block_address_block拆出file_id和block_id - 用这两个值查
DBA_EXTENTS,条件必须是&block_id BETWEEN block_id AND block_id + blocks - 1(不是等于) - 若查到
SEGMENT_TYPE = 'LOBSEGMENT'或'ROLLBACK',说明争用来自 LOB 字段或回滚段——这两类比普通表更易触发 HW 竞争 - 若
P2 = 1且对应表空间是UNDO,实际是回滚段在频繁扩展,和业务表无关
为什么预分配 extent 只能临时缓解?
手动 ALLOCATE EXTENT 绕过了自动扩展时的 HW 锁排队,但治标不治本:
- 对普通表:执行
ALTER TABLE t ALLOCATE EXTENT (SIZE 100M)有效;但对 LOB 段,得先查DBA_LOBS找真实段名(如SYS_LOB0000098765X$$),再对其操作 - 预分配大小建议 ≥ 当前最大 extent,查法:
SELECT DISTINCT BYTES FROM DBA_EXTENTS WHERE SEGMENT_NAME = 'xxx' - 该操作不释放已有碎片,也不影响后续 DML 分布;若表已严重碎片化,只是“打补丁”
- RAC 环境中,extent 分配仍需跨实例协调,单节点预分配无法消除全局 HW 锁争用
分区(尤其是 hash 分区)为什么比调参数更有效?
HW 锁是按 segment 粒度串行化的,分区把一个段拆成多个物理段,天然分流 HWM 推进请求:
- 对高并发
INSERT场景:CREATE TABLE t (...) PARTITION BY HASH(id) PARTITIONS 8可将 HW 锁争用降低 70%+,前提是分区键能均匀分布写入 - LOB 字段必须配合表分区才能让 LOB 段也分区;单独对 LOB 列建索引或改存储参数无效
- Oracle 12c 支持在线重定义(
DBMS_REDEFINITION)做不停机分区,但要求主键或唯一约束存在,且不能有未完成的物化视图日志
真正容易被忽略的是:HW 争用常和 buffer busy waits(P3=4 段头争用)或 gc buffer busy acquire(RAC 下跨实例块争用)混发,必须先分离等待类型再动手——否则加 freelist、扩 undo、调 cache_size 全是白忙。











