“read by other session”高表明逻辑块缓存争用而非i/o慢;需查v$session_wait确认是否真热点(file#/block#是否集中),再通过dba_extents或x$bh定位对象;多数根因是统计信息过期导致执行计划劣化。

read by other session 在 Oracle 11g 的 AWR 报告里排高,说明不是 I/O 慢,而是多个会话在争抢同一组逻辑数据块的缓存加载权。它本质是“读竞争”,不是“磁盘慢”。直接加内存、换存储、调 db_cache_size 基本无效。
先确认是不是真热点:查 v$session_wait 的 file#/block# 是否集中
只看等待次数没用,关键看是否反复落在同一 file# 和 block# 上:
SELECT p1 "file#", p2 "block#", p3 "class#", COUNT(*) cnt FROM v$session_wait WHERE event = 'read by other session' GROUP BY p1, p2, p3 ORDER BY cnt DESC FETCH FIRST 5 ROWS ONLY;
如果前几行的 file#=5, block#=123456 占了 80%+ 等待,就是典型热块;如果结果分散,大概率是大量 SQL 全表扫描或索引快速扫描(db file scattered read)导致的预读冲突。
- 集中 → 锁定对象:用
dba_extents查这个 block 属于哪个段 - 分散 → 查 AWR 中 TOP SQL,看是不是统计信息陈旧引发执行计划劣化
定位热块对应的真实对象:别用 OBJECT_ID 直接关联
X$BH 和 v$bh 里的 obj 字段对应的是 DATA_OBJECT_ID,不是 OBJECT_ID。错用 dba_objects.object_id 会查到错误对象甚至空结果:
SELECT owner, segment_name, segment_type FROM dba_extents WHERE file_id = &FILE_ID AND &BLOCK_ID BETWEEN block_id AND block_id + blocks - 1;
更准的方式是结合 X$BH:
SELECT o.owner, o.object_name, o.object_type FROM x$bh b, dba_objects o WHERE b.obj = o.data_object_id AND b.file# = &FILE_ID AND b.dbablk = &BLOCK_ID;
如果返回为空,该块可能属于:undo、temp、系统段,或已 DROP 但未清理的对象,需进一步查 v$database_block_corruption 或 v$tempseg_usage。
区分是 scattered read 还是 sequential read 引发的争用
read by other session 几乎总伴随 db file scattered read(多块读)或 db file sequential read(单块读),但优化路径完全不同:
- 若 AWR 中
db file scattered read高 → 多是全表扫描或索引快速扫描(INDEX FAST FULL SCAN)导致,重点查缺失谓词、统计信息不准、是否应走索引 - 若
db file sequential read高 → 更可能是索引唯一扫描或回表访问卡在某个索引块(比如右端新增索引块、主键索引根块),考虑重建索引、调整PCTFREE、拆分热点索引
特别注意:Oracle 11g 中,WRH$_SQL_BIND_METADATA 表因绑定变量过多,可能让 MMON 扫描 x$kqlfbc 超时,间接拖慢快照生成,使 AWR 报告本身失真——此时先确认 SYSAUX 空间和 MMON 状态是否正常,再分析等待事件。
别跳过统计信息检查:这是最常被忽略的根因
很多 case 表面是热块,实际是执行计划退化后,SQL 开始疯狂扫同一张表的高水位线附近块。例如:
SELECT COUNT(*) FROM orders WHERE status = :b1 AND create_time > :b2;
如果 orders 表的 status 列统计信息过期,优化器可能误判为高选择性,放弃索引而走全表扫描,所有会话就一起涌向那几个数据块。
查统计信息最后更新时间:
SELECT owner, table_name, last_analyzed, num_rows
FROM dba_tables
WHERE table_name IN ('ORDERS', 'TABLE2')
AND owner = 'APP_OWNER';
收集时别只跑默认参数,尤其对大表要显式指定采样率和列直方图:
EXEC DBMS_STATS.GATHER_TABLE_STATS( ownname => 'APP_OWNER', tabname => 'ORDERS', estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, method_opt => 'FOR ALL COLUMNS SIZE AUTO', cascade => TRUE);
真实生产中,90% 的 read by other session 高发场景背后,都藏着一个没及时更新的统计信息,或者一个没加 hint / profile 锁住的劣化执行计划。别急着调参,先确认 SQL 是不是在走它该走的路。











