<p>ASH可直接定位序列争用热点,通过查询gv$active_session_history中enq: SQ - contention事件并关联dba_objects,按current_obj#聚合统计出现频次,即可识别高争用序列。</p>
序列竞争在 oracle 中通常表现为 enq: sq - contention 等待事件集中爆发,ash 是定位这类瞬时、高频率争用最直接有效的手段——不是靠猜,而是看谁在什么时间、对哪个序列反复排队。
怎么从 ASH 里快速揪出热点序列
序列争用本质是会话在获取 NEXTVAL 时卡在队列锁上,对应 ASH 中的 enq: SQ - contention 或偶尔出现的 DFS lock handle(RAC 环境下)。关键不是等它“慢”,而是抓它“挤”:
- 直接查
V$ACTIVE_SESSION_HISTORY,过滤event为enq: SQ - contention,并提取p1text/p1(即被争用的序列对象编号) - 用
dba_objects.object_name关联p1(注意:p1是object_id,不是data_object_id),就能定位到具体序列名 - 别只看单次采样,要聚合统计:同一
sql_id+ 同一current_obj#出现频次越高,越可能是瓶颈源头
示例 SQL(sys 租户下执行):
SELECT o.owner, o.object_name, o.object_type, COUNT(*) cnt, MIN(sample_time) first_seen, MAX(sample_time) last_seen FROM gv$active_session_history a JOIN dba_objects o ON a.current_obj# = o.object_id WHERE a.event = 'enq: SQ - contention' AND a.sample_time > SYSDATE - 10/1440 GROUP BY o.owner, o.object_name, o.object_type ORDER BY cnt DESC;
为什么 current_obj# 能对应到序列,但有时为空?
current_obj# 字段在 ASH 中记录的是会话当前正在操作的对象 ID。对序列而言,它在 enq: SQ - contention 等待期间确实会填入对应序列的 object_id ——但前提是该等待发生在 DML 或 PL/SQL 调用 NEXTVAL 的上下文中,且未被优化跳过对象引用。
- 纯 SQL 语句如
INSERT INTO t VALUES (seq.NEXTVAL, ...)一般能捕获到current_obj# - 如果序列值由应用层缓存后批量插入(如 MyBatis 的
useGeneratedKeys+ 批量 insert),或使用了SEQUENCE.CURRVAL,则 ASH 可能不记录current_obj#,此时需结合sql_id反查DBA_HIST_SQLTEXT或实时V$SQL定位调用方 - RAC 环境下,若争用发生在全局队列协调阶段,
current_obj#可能为 0 或无效值,这时重点看p2(sequence cache size 相关)和p3(sequence increment)辅助判断
排查时容易忽略的两个硬伤
序列争用常被误判为“SQL 慢”,但实际根源在设计层。ASH 能暴露现象,但不能自动修复:
- 序列的
CACHE值过小(比如默认 20)会导致频繁重载,尤其在高并发 INSERT 场景;ASH 报告里若看到大量enq: SQ - contention集中在极短时间内,大概率是 cache 不够用,而不是 SQL 写得差 - 应用没做批量提交,而是每条记录都调一次
seq.NEXTVAL,哪怕序列 cache=1000,也会因事务粒度太细引发排队;此时 ASH 中session_id和sql_id高度离散,但event和sample_time密集堆叠——这是典型的“自己堵自己”
真正要解决,得改序列定义(增大 CACHE)、改应用逻辑(批量取号)、或换方案(如 UUID、应用层 ID 生成器)。ASH 只负责告诉你:堵在哪、谁在堵、堵得多狠。


















