table fetch continued row 高说明表中存在大量行迁移,即UPDATE导致行被整行搬移至新数据块,访问时需额外I/O拼合,反映在堆表的ROWID访问路径中,非行链接或索引问题。
AWR 中 table fetch continued row 高说明什么
这个统计值高,基本等于在说:表里存在大量行迁移(row migration)——不是行链接(row chaining),是同一行被拆开放在不同数据块里,且后续访问需要额外 i/o 才能拼完整。它不反映索引扫描问题,只出现在堆表的 table access by index rowid 或全表扫描路径中。
常见错误现象:table fetch continued row 每秒几十甚至上百次,但 db block gets 和 consistent gets 明显偏高,同时 physical reads 却没同比飙升,说明热点行反复被“跳着读”;配合 v$segment_statistics 查具体对象,常会发现某张大表的 logical reads 远高于其 buffer busy waits 或 read by other session,这是迁移而非争用的典型信号。
- 只对堆表有效;IOT、分区索引组织表、外部表不计入此指标
- Oracle 10g 及以后版本才稳定采集,9i 下该值常为 0 或严重低估
- 单次迁移行被访问 N 次,该计数就 +N,不是“迁移行数量”,而是“被迫二次定位的次数”
怎么从 AWR 报告定位哪张表在迁移
别直接翻 AWR HTML 报告首页的 Top SQL —— table fetch continued row 是实例级累计值,必须下钻到 Segment Statistics 部分。打开 AWR 报告后,按 Ctrl+F 搜 Segment Statistics,找到 “Physical Reads” 或 “Logical Reads” 排名靠前的段,再看同一节里的 Table Fetch Continued Row 列(有些版本叫 Table Fetch By Rowid 子类)。
更准的做法是查 dba_hist_seg_stat:
SELECT owner, object_name, object_type,
SUM(CASE WHEN stat_name = 'table fetch continued row' THEN value ELSE 0 END) AS continued_fetches
FROM dba_hist_seg_stat s
JOIN dba_hist_snapshot sn ON s.snap_id = sn.snap_id
WHERE sn.begin_interval_time > SYSDATE - 1
AND object_type = 'TABLE'
GROUP BY owner, object_name, object_type
HAVING SUM(CASE WHEN stat_name = 'table fetch continued row' THEN value ELSE 0 END) > 1000
ORDER BY continued_fetches DESC;- 注意时间范围:必须跨至少两个快照,否则
dba_hist_seg_stat可能为空 - 过滤
object_type = 'TABLE'很关键,索引段也会有该统计但无实际意义 - 如果结果为空,检查是否启用
STATISTICS_LEVEL = TYPICAL(BASIC下不收集段级统计)
ANALYZE TABLE ... LIST CHAINED ROWS 能不能查出行迁移
不能。这个命令只捕获行链接(chained rows),即 INSERT 时单行就超过块大小,被迫跨块存储的情况。而行迁移(migrated rows)是 UPDATE 导致原块放不下、整行被搬走,LIST CHAINED ROWS 对它完全无感。
真正能抓迁移行的是 v$session_longops 配合 DBMS_ROWID,但太重;生产环境更推荐直接查 CHAINED_ROWS 视图的补充字段或用以下轻量方式:
SELECT COUNT(*) FROM your_table WHERE ROWID IN ( SELECT CHAIN_ROWID FROM CHAINED_ROWS WHERE OWNER = 'YOUR_SCHEMA' AND TABLE_NAME = 'YOUR_TABLE' );
- 先确保已运行过
ANALYZE TABLE ... LIST CHAINED ROWS建好CHAINED_ROWS表 - 上面 SQL 返回非零 ≠ 有迁移行,只是说明链式行存在;迁移需结合
dba_tab_partitions的avg_row_len与pct_free对比判断 - 最可靠方式:查
v$sysstat中table fetch continued row增速,再对比对应表的UPDATE频率 —— 如果更新密集且pct_free设置过低(比如
修复迁移行时最容易被忽略的点
重建表(ALTER TABLE ... MOVE)或收缩(SHRINK SPACE COMPACT)能清理迁移行,但做完之后必须立刻做两件事:更新统计信息、重建受影响的索引。
-
ALTER TABLE t MOVE后,所有基于原 ROWID 的索引会失效(STATUS = UNUSABLE),不重建就查不到数据 -
SHRINK SPACE要求表启用行移动(ENABLE ROW MOVEMENT),否则报错ORA-10636: row movement is not enabled - 即使用了
SHRINK SPACE CASCADE,也不会自动更新统计信息 ——DBMS_STATS.GATHER_TABLE_STATS必须手动触发,否则执行计划可能继续走错路 - 迁移行多通常意味着频繁 UPDATE + 小
pct_free,修复后若不调高pct_free(比如从 10 改成 20),几周后又会复发
迁移本身不伤数据一致性,但它把随机 I/O 变成固定模式的双块访问,CPU 和缓存压力会悄悄变大;等看到 table fetch continued row 明显上升,往往已经有几百行在迁移了。

















