enq: TX - index contention本质是索引右热点块分裂时的TX mode 4串行化锁争用,主因是单调递增键值导致所有INSERT挤向最右叶块,触发90-10分裂并排队等待。
这个等待事件不是索引分裂本身有问题,而是多个会话在争抢同一个索引块的分裂权限——本质是热点块串行化锁(tx mode 4)卡住了并发插入。
为什么高并发 INSERT 会卡在 enq: TX - index contention
核心原因是索引键值单调递增(比如 SEQUENCE.NEXTVAL 或 SYSDATE),所有新数据都挤向索引最右侧的叶块。当该块满时触发 9-1 split,Oracle 必须用一个独占的 TX enqueue(mode 4)来序列化分裂操作。其他会话只要还想往同一块插,就只能等。
- 现象:AWR 中
enq: TX - index contention等待时间飙升,同时leaf node 90-10 splits计数暴涨 - 关键识别点:
v$session_wait中P3值含0x2000000标志,且P1='TX|4' - 不是磁盘慢、不是 buffer cache 不足,而是逻辑结构上的“排队上厕所”——只有一个门,所有人得轮流进
反向键索引(REVERSE KEY)真的能解决?
能,但有代价。它把 10001 → 10001 变成 10001 → 10001(字节级反转),让原本集中在右端的插入散落到多个叶块。
- 适用场景:主键/唯一索引纯用于等值查询(如
WHERE id = ?),不依赖范围扫描(BETWEEN、>) - 不适用场景:业务 SQL 大量使用
ORDER BY id DESC或分页查询,REVERSE会让范围扫描退化为全索引扫描 - 操作示例:
CREATE INDEX idx_t_id_rev ON t(id) REVERSE;,注意不能对函数索引或位图索引使用 - 重建后需验证:
SELECT /*+ INDEX(t idx_t_id_rev) */ * FROM t WHERE id = 12345;看执行计划是否仍走索引
SEQUENCE 的 CACHE 大小影响有多大?
影响间接但关键。小 CACHE(如默认 20)会导致频繁访问 SEQ$ 表和日志写,拖慢事务提交速度,变相延长了持有 TX enqueue 的时间窗口。
- 建议设为
CACHE 1000或更高(根据每秒插入峰值估算);但要注意实例崩溃会丢失未用完的 cache 值 - 不要只调
CACHE:它缓解的是序列获取瓶颈,不是索引分裂瓶颈。若enq: TX - index contention仍是 top 等待,说明热点还在索引块上 - 配合检查:
SELECT sequence_name, cache_size, order_flag FROM dba_sequences WHERE sequence_name = 'XXX';
分区索引比反向键更稳?
对 OLTP 场景,HASH 分区索引通常比 REVERSE 更实用,尤其当无法改造 SQL 时。
- 原理:用
HASH(id)把递增值打散到多个分区,每个分区有自己的右热点,互不干扰 - 最小成本方案:
CREATE INDEX idx_t_id_hash ON t(id) GLOBAL PARTITION BY HASH(id) PARTITIONS 8; - 注意:必须是
GLOBAL分区(本地分区无法解决单表插入热点),且分区数建议 2 的幂次(4/8/16),避免哈希倾斜 - 风险点:分区数太多会增加解析开销;太小(如 2)仍可能有局部热点,需结合
dba_tab_partitions的num_rows分布验证
真正容易被忽略的是:enq: TX - index contention 往往和 buffer busy waits 同时出现,但后者是结果,前者才是根因。别急着调 db_cache_size,先确认是不是索引设计把所有 INSERT 都逼到了同一块物理页上。











