根本原因是多个并行进程争抢同一组热数据块,尤其在扫描未分区大表或共享索引根/分支块时;可通过v$session定位等待会话及热点块,再结合dba_extents定位对象,优化手段包括分区、禁用低效索引、转本地索引等。
根本原因不是并行本身,而是多个并行进程争抢同一组热数据块——尤其当它们反复扫描未分区的大表、或共享索引根/分支块时。
查当前正在等的并行会话和对应SQL
直接看活跃会话里哪些在等 read by other session,且属于并行执行(QC_SESSION_ID 非空 或 px_qcsid 有值):
- 运行:
SELECT sid, serial#, sql_id, event, p1 "file#", p2 "block#", p3 "class#", qc_session_id FROM v$session WHERE event = 'read by other session' AND (qc_session_id IS NOT NULL OR server# LIKE 'P%') - 重点观察
p1/p2是否高度重复:如果多个会话的file#=14且block#=2777448总是一起出现,说明这是热点块 - 用
p1/p2反查对象:SELECT owner, segment_name, segment_type FROM dba_extents WHERE file_id = 14 AND 2777448 BETWEEN block_id AND block_id + blocks - 1
确认是否是并行DML或并行查询触发的块争抢
并行加载(如 INSERT /*+ APPEND PARALLEL(4) */)本身不直接导致该等待,但以下场景会放大风险:
- 目标表没有分区,所有并行进程都往同一个高水位线(HWM)后插入,争抢相同的数据块(尤其是段头块、L1/L2位图块)
- 源表是大单表全扫,且没走分区裁剪,多个PX进程反复读同一组索引叶子块或表数据块(比如扫描一个 500GB 的未分区订单表)
- 使用了全局索引的并行 DML:每个 PX 进程都要更新同一个索引结构,导致索引根块/分支块成为瓶颈
-
PARALLEL_INDEX提示强制走索引,但该索引选择性差,大量进程命中同一片叶子块范围
快速验证并行度与等待量的线性关系
别急着改 SQL,先做一次控制变量测试:
- 临时把问题 SQL 的并行度降到 1:
ALTER SESSION DISABLE PARALLEL DML; ALTER SESSION FORCE PARALLEL QUERY PARALLEL 1; - 重跑相同逻辑,观察
v$session_event中read by other session等待是否消失或大幅下降 - 若等待锐减,说明原并行度(比如
PX进程数 > 4)已超出 I/O 子系统或 buffer cache 的承载能力,不是“越并行越快” - 注意:某些存储(如 NFS、低配 ASM diskgroup)在并发 8+ 读请求时,
db file scattered read响应时间会陡增,间接拉长read by other session等待
绕过热点块的实操手段
真正能见效的不是调参数,而是拆冲突点:
- 对加载目标表启用
SEGMENT CREATION DEFERRED并按时间/业务维度预建分区,避免所有 PX 进程挤在同一个 segment 上 - 源表全表扫描类操作,加
/*+ NO_PARALLEL_INDEX(t idx_name) */强制走全表扫描 + 分区裁剪,比走低效全局索引更稳 - 若必须走索引,把全局索引转为本地索引(
LOCAL),让每个 PX 进程只操作自己负责分区的索引段 - 检查
buffer_pool_keep是否被误用:把小而热的配置表放 KEEP 池本意是好的,但如果多个并行任务同时访问它,反而加剧read by other session—— 此类表更适合用结果集缓存(RESULT_CACHE)
最常被忽略的一点:即使你关了并行,只要应用层仍用连接池发起大量相同 SQL(比如每秒 50 次查配置表),read by other session 依然会出现。并行只是把“串行排队”显性化成“并行排队”,根子还在 SQL 访问模式和对象物理布局上。


















