

























平时跟数据库打交道,OID 和 ROWID 这俩“隐藏字段”经常半路冒出来,看着很像,用起来差得不是一星半点。很多人要么把它俩混为一谈,要么只知道能用、却不知道为啥这么设计、踩过哪些坑。

本文结合实测案例,从KingbaseES的底层原理、全局 / 局部特性、创建方式、类型别名到实战坑点,完整拆解 OID 与 ROWID 的核心知识,帮你彻底分清两者边界,避开生产环境常见误用。
OID 就是 Object Identifier,对象标识符。你可以把它理解成数据库内部给各种对象发的唯一工号——表、函数、类型、索引、视图……全都有一个。
它是 4 字节整数,数据库内核靠它管理元数据,系统表基本都拿 OID 当主键。
很多人一上来就懵:为啥有时候 OID 全局唯一,有时候又只在表里自增?
其实很简单:
默认建表,OID 是不存在的。你直接 select oid from 表 直接报错:列不存在。
想让表带上 OID,只有两种方式:
default_with_oids = truecreate table ... with oids;开了之后它还是“隐藏列”,select * 不显示,必须手动写 oid 字段才能查出来。
写系统表查询时最烦的一件事:
想查一张表的字段,得先去 sys_class 查 OID,再关联 sys_attribute。
有了 regclass 就舒服多了:
-- 直接把表名转成对应 OID
select * from sys_attribute where attrelid = 'teachers'::regclass;
::regclass 本质就是 OID 的别名,相当于帮你自动执行了一次:
select oid from sys_class where relname = 'teachers';
系统表查询瞬间清爽很多,这也是日常开发里 OID 最实用的姿势。
别看它能唯一标识,别拿来当业务主键用:
正经做主键,用 serial 或 bigserial 比它靠谱得多。
如果说 OID 管的是对象,那 ROWID 管的就是行。
它是数据库给每一行记录分配的逻辑唯一标识,不是物理地址,但能做到全局唯一、快速定位,有点像 Oracle 里那味儿,但底层实现是自研逻辑。
ROWID 最爽的一点:
你只要 with rowid 建表,数据库自动给你建一个唯一 btree 索引。
create table student(...) with rowid;
查完表结构你会看到:
Indexes:
"student_rowid_key" UNIQUE CONSTRAINT, btree ("rowid")
查询直接按 ROWID 定位,速度极快。
ROWID 有三种启用姿势:
default_with_rowid = truewith rowidalter table ... set with rowid;这里有个重要规则:
ROWID 优先级 > OID
两个参数同时开,只生效 ROWID,OID 直接被屏蔽。
如果你开了 OID、想强行建 ROWID 表,数据库会直接报错阻止你,避免混乱。
你查出来大概是这种:
AAAAAAAAADP5AAAAAAAAAAA
一共 23 个字符,用 64 进制编码(A-Z、a-z、0-9、+/),内部拆成三部分信息:
所以它是严格单调递增的:后插的行,ROWID 一定更大。
支持所有比较运算符:
select * from t where rowid > 'xxx';
排序、分组、where 条件都能用,非常丝滑。

| 对比项 | OID | ROWID |
|---|---|---|
| 管什么 | 数据库对象(表、函数、类型) | 数据行记录 |
| 作用范围 | 系统表全局,普通表局部 | 全局逻辑唯一 |
| 数据类型 | 4 字节整数 | 23 位 64 进制字符串,变长存储 |
| 默认可见性 | 完全隐藏,* 不显示 |
隐藏,需显式查询 |
| 自动索引 | 无 | 自带唯一 btree 索引 |
| 开启方式 | with oids / 参数开关 |
with rowid / 参数开关 / alter |
| 与对方关系 | 优先级低,会被 ROWID 覆盖 | 优先级高,互斥生效 |
| 单调递增 | 系统表全局递增,表内从 1 开始 | 严格全局递增,后插更大 |
| 适合场景 | 系统表查询、元数据管理 | 快速行定位、批量处理、高效查询 |
| 风险点 | 42 亿后绕回,可能重复 | 几乎无上限,稳定性更强 |
| 业务主键建议 | 强烈不推荐 | 可用于内部快速定位,不建议业务暴露 |
文章里一堆 ::,比如 'teachers'::regclass、'25'::integer。
它就是强制类型转换,和 CAST( … AS …) 完全等价,只是写法更短。
-- 这俩效果一模一样
select '25'::integer;
select cast('25' as integer);
在系统表查询、特殊类型转换里,:: 几乎是标准写法,看着专业,写着省事。
:: 是写系统 SQL 的神器,学会少写很多嵌套子查询。整体理解下来就一句话:
OID 管对象,ROWID 管行;一个管元数据,一个管提速。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。