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 的时间窗口。
oracle知识库
oracle知识库下载
下载
建议设为
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 都逼到了同一块物理页上。