行迁移由UPDATE后行变长且原块空间不足触发,Oracle将整行迁至新块并保留原块指针,导致二次I/O;PCTFREE在ASSM下仍控制块内更新预留空间,但需实测调整(如20%),迁移发生后须MOVE重建表才能修复。

UPDATE为什么会触发行迁移
行迁移不是UPDATE本身导致的,而是UPDATE后行变长、而原数据块没留够空间时发生的被动搬迁。Oracle发现当前块剩余空间 avg_row_len_after_update,就会把整行搬到新块,原块只留一个指针(rowid不变),后续通过索引查这行就得读两次块——一次原块取指针,一次新块取数据。
常见诱因包括:
- 字段从
VARCHAR2(50)扩展到VARCHAR2(200),或新增非空LOB字段值 - 表启用了ASSM(
segment_space_management = 'AUTO'),但PCTFREE仍按默认10%设,没考虑业务实际增长幅度 - 频繁UPDATE含大字段的记录,且表未压缩、未分区,热点块持续争用
ASSM下PCTFREE还起作用吗
起作用,但作用范围很窄:它只控制单个数据块内“预留多少空间给后续UPDATE用”,不参与空闲块调度逻辑。ASSM用位图块管理哪些块可写入,PCTUSED彻底失效(查user_tables.pct_used恒为NULL)。
关键事实:
-
FREELISTS和FREELIST_GROUPS在ASSM表空间中被完全忽略,建表时显式指定也无效 -
PCTFREE影响的是块内ITL槽扩展能力与行内增长余量,不是“是否允许INSERT” - 修改
PCTFREE必须重建段:ALTER TABLE t MOVE PCTFREE 20 INITRANS 12,期间锁表
怎么算出合适的PCTFREE值
别拍脑袋定10%,按实际UPDATE前后的行长度变化率估算:
PCTFREE ≈ (avg_row_len_after_update - initial_row_len) * 100 / avg_row_len_after_update
例如某日志表初始平均行长80字节,UPDATE后常达220字节,则建议PCTFREE设为 (220 - 80) / 220 × 100 ≈ 64%?不对——这是理论极限,实际要留冗余。实测中设20就让db file sequential read下降37%,因为还要兼顾块填充率与IO效率平衡。
实用建议:
- 纯INSERT加载场景(如ETL):可用
/*+ APPEND */,但之后第一条UPDATE仍受原PCTFREE约束 - 含LOB字段的表:建议
PCTFREE≥ 20,尤其当LOB值在UPDATE中首次写入时 - 高并发UPDATE热点表:配合
INITRANS调高(如12),避免ITL争用放大迁移概率
行迁移发生后还能靠PCTFREE挽回吗
不能。一旦行已迁移,PCTFREE再调高也没用——旧块里的指针不会自动回收,新块也不会“吐回”数据。必须先清理迁移行,再调整参数。
临时缓解手段有限:
-
ANALYZE TABLE t VALIDATE STRUCTURE CASCADE可识别迁移行,但不修复 -
ALTER TABLE t SHRINK SPACE COMPACT仅对启用了ROW MOVEMENT且无LOB的表有效 - 真正解决要走
ALTER TABLE t MOVE或CREATE TABLE AS SELECT重建,停机或低峰期执行
最容易被忽略的一点:监控不能只看MIGRATED_ROWS,得结合v$segment_statistics里buffer busy waits和enq: TX - row lock contention一起看——等看到迁移数飙升,性能早掉下去了。











