ROWID查询只需一次I/O因直接定位物理地址(文件号、块号、行号),跳过索引查找;主键查询需索引遍历+回表,至少两次I/O;ROWID更新省去索引扫描与回表,但依赖事务内有效、同库、非IOT等前提。

ROWID查询为什么只要一次I/O就能拿到数据
因为ROWID直接编码了数据在磁盘上的物理位置:文件号、块号、行号。Oracle执行 SELECT * FROM t WHERE ROWID = 'AAAS7gAABAAANZkAAA' 时,跳过索引查找环节,直接按地址读取对应数据块里那一行——整个过程只需一次物理读(Physical Read)。而主键查询必须先走索引(比如B+树),定位到叶子节点后拿到ROWID,再回表查一次,至少两次I/O。
主键更新要多走哪些路
用主键更新,比如 UPDATE users SET name = 'x' WHERE id = 101,Oracle实际执行路径是:
- 扫描主键索引,找到
id = 101对应的索引条目 - 从该索引条目中取出这一行的
ROWID - 根据这个
ROWID去数据块里定位并锁定那行记录 - 执行修改、写入Undo/Redo
这中间涉及索引遍历、回表、行锁获取等多个步骤。而用 WHERE ROWID = ? 更新,省掉了前两步,直奔目标行。
ROWID更新的硬性前提和常见翻车点
ROWID快,但不是无条件快。它依赖几个隐含前提,漏掉一个就可能变慢甚至出错:
-
ROWID必须来自同一事务或极短时间内获取——一旦发生ALTER TABLE MOVE、表压缩、分区分裂,旧ROWID就失效,再用会报ORA-01410: invalid ROWID - 不能跨数据库使用:
ROWID是 Oracle 堆表专属,MySQL/PostgreSQL/KES(未开启default_with_rowid)里写WHERE ROWID = ...直接语法报错 - 不能用于索引组织表(IOT),IOT 的
ROWID实际是逻辑主键值,行为完全不同 - 批量更新时若只靠
MIN(ROWID)和MAX(ROWID)划分范围,遇到高并发插入可能导致部分行被跳过——ROWID不保证严格递增
什么时候真该用ROWID而不是主键
典型场景非常具体,不是“能用就用”,而是“非它不可”:
- 高并发下基于游标做增量同步:比如监听日志表,每次取最后一条的
ROWID,下次从它之后继续扫,避免重复或遗漏 - 删除完全重复的数据行:
SELECT ROWID, col1, col2 FROM t GROUP BY col1, col2 HAVING COUNT(*) > 1,再对多余ROWID执行DELETE - 大表分批更新且已通过子查询锁定目标集:先
SELECT ROWID FROM t WHERE status = 'pending' AND ROWNUM ,再用这批 <code>ROWID更新,比反复查条件快得多
真正容易被忽略的是:ROWID不是性能银弹,它是把双刃剑——快的前提是稳定,而稳定性恰恰最脆弱。业务层如果误把它当成长期标识存进日志或MQ消息,后续表迁移或备份恢复后,所有基于它的逻辑都会静默失败。


















