在构建后端架构或微服务系统时,数据唯一标识是绕不开的核心问题。金仓数据库(KingbaseES)提供了三种方案:OID、ROWID 和 自增主键。它们各司其职,选错了可能影响查询性能或运维效率。本文从原理到实战,帮你理清三者的区别与适用场景。
一、OID:数据库的全局身份证
OID(Object Identifier)是数据库内部的4字节无符号整数,为每个对象(表、视图、索引等)分配唯一编号。在系统目录中,OID是全局唯一的,例如 sys_class、sys_attribute 等系统表通过OID互相关联,形成元数据网络。
但普通用户表默认不携带OID,需要显式启用:
-- 看看 sys_type 里有多少条记录
test=# SELECT count(*) FROM sys_type;
count
-------
1208
(1 row)
-- 其中超过半数的 OID 都大于 1208,说明系统预分配了大量 OID
test=# SELECT count(*) FROM sys_type WHERE oid > 1208;
count
-------
1120
(1 row)
-- 再看看 OID 最大值附近,还有在持续分配的
test=# SELECT oid FROM sys_type WHERE oid > 33990;
oid
-------
33992
33993
33995
(3 rows)
OID在系统表中是全局共享,在普通表中却是局部自增——表A的OID=1与表B的OID=1毫无关联。这种设计让OID更适合DBA运维场景,而非业务标识。
regclass 类型是OID的“翻译官”,可将表名自动转为OID,简化元数据查询:
-- 创建一个复合类型
cpbd_test=> CREATE TYPE aatyp IS TABLE OF int;
CREATE TYPE
cpbd_test=> SELECT oid, typname FROM sys_type WHERE typname = 'aatyp';
oid | typname
-------+---------
55136 | aatyp
(1 row)
-- 再创建一个函数
cpbd_test=> CREATE OR REPLACE FUNCTION func_test05(i int)
RETURN int AS vv int; BEGIN RETURN 1; END;
CREATE FUNCTION
cpbd_test=> SELECT oid, proname FROM sys_proc WHERE proname = 'func_test05';
oid | proname
-------+------------
55137 | func_test05
(1 row)
二、ROWID:KES独有的行定位利器
ROWID是金仓数据库独创的行标识机制,以隐藏列形式物理存储,并自动创建B-tree唯一索引。与OID不同,ROWID不仅标识行身份,还记录了物理位置,支持范围查询(BETWEEN、> 等)。
启用方式有三种:
- 全局参数:设置
default_with_rowid = on - 建表指定:
CREATE TABLE ... WITH (ROWID) - 后期追加:
ALTER TABLE ... SET ROWID
test=# CREATE TABLE tt5(id int);
CREATE TABLE
test=# INSERT INTO tt5 VALUES(10);
INSERT 0 1
test=# SELECT oid, id FROM tt5;
ERROR: column "oid" does not exist
LINE 1: select oid,id from tt5;
^
HINT: Perhaps you meant to reference the column "tt5.id".
⚠️ 注意:OID与ROWID互斥,一张表不能同时拥有两者。
三、ROWID内部结构拆解
ROWID显示为23字符的Base64编码字符串,实际存储仅需4-18字节(变长编码)。结构分为三段:
test=# SET default_with_oids TO true;
SET
test=# CREATE TABLE tt6(id int);
CREATE TABLE
test=# INSERT INTO tt6 VALUES(10);
INSERT 0 1
test=# SELECT oid, id FROM tt6;
oid | id
-----+----
1 | 10
(1 row)
| 段 | 长度 | 含义 |
|---|---|---|
| 块号 | 前12字符 | 物理块ID |
| 事务ID | 中间部分 | 生成时的事务标识 |
| 命令计数器 | 后11字符 | 事务内语句序号 |
| 段 | 字符位置 | 位数 | 干什么用的 |
|---|---|---|---|
| 存储位置(块号) | 第 1~6 字符 | 32 位 | 这行数据存在哪个存储块 |
| 事务 ID(xid) | 第 7~12 字符 | 32 位 | 插入这行数据的事务编号 |
| 命令计数器 | 第 13~23 字符 | 64 位 | 同一个事务里第几条命令 |
ROWID支持完整比较操作,且与COPY导入导出兼容,适合数据迁移场景。
四、三方案选型对比
| 维度 | OID | ROWID | 自增主键 |
|---|---|---|---|
| 作用域 | 全局(系统表) | 表级 | 表级 |
| 自动索引 | 无 | B-tree唯一索引 | 需手动创建 |
| 范围查询 | 不支持 | 支持 | 支持 |
| 业务关联 | 弱 | 弱 | 强 |
| OID | ROWID | SERIAL / BIGSERIAL | |
|---|---|---|---|
| 是什么 | 系统伪列,可选开启 | 物理存储的隐藏列 | 用户自己定义的显式列 |
| 数据类型 | 4 字节无符号整数 | Base64 字符串(rowidtype) | 整数(int4 或 int8) |
| 唯一范围 | 系统表全局唯一;普通表仅表内 | 仅表内 | 仅表内 |
| 物理存储 | 元组头部,不占用户列空间 | 隐藏列,变长 4~18 字节 | 用户列,固定 4 或 8 字节 |
| 索引 | 无自动索引 | 自动建 B-tree 唯一索引 | 需要手动建主键或唯一约束 |
| 上限 | ~42.9 亿(有回卷风险) | 编码空间远大于 OID | SERIAL ~21 亿;BIGSERIAL ~9.2×10^18 |
| 适合干什么 | 系统目录查询 | 行级定位、快速查找 | 业务主键、外键关联 |
| 参数控制 | 不需要 GUC 参数 |
✅ 选型建议:
- 系统目录查询 → OID + regclass
- 快速行定位、调试 → ROWID
- 业务唯一标识 → SERIAL/BIGSERIAL + PRIMARY KEY
五、GUC参数配置与生产避坑
与OID/ROWID相关的两个GUC参数:
| 参数 | 类型 | 默认值 | 作用 |
|---|---|---|---|
| BOOL | false | 新建表是否默认带 OID 列 | |
| BOOL | false | 新建表是否默认带 ROWID 列 |
四种组合的建表行为:
| 建出来的表 | ||
|---|---|---|
| false | false | 什么都没有——普通表 |
| true | false | 带 OID,没有 ROWID |
| false | true | 带 ROWID,没有 OID |
| true | true | ROWID 赢——带 ROWID,没有 OID |
唯一会报错的组合是:全局关闭ROWID、开启OID,却试图创建带ROWID的表。错误信息会明确提示冲突。
生产环境五条建议:
- 集群统一配置
default_with_rowid和default_with_oid - 优先使用ROWID而非OID作为行标识
- DDL中显式声明,不依赖全局参数
- 同一数据库内避免混用OID和ROWID
- 业务主键不可替代,OID/ROWID仅是内部手段
六、实战:用OID串联系统目录
OID在元数据查询中扮演“粘合剂”角色。核心系统表关系如下:
test=# SET default_with_oids TO false;
SET
test=# CREATE TABLE tt7(id int) WITH OIDS;
CREATE TABLE
test=# INSERT INTO tt7 VALUES(10);
INSERT 0 1
test=# SELECT oid, id FROM tt7;
oid | id
-----+----
1 | 10
(1 row)
实战场景示例:
- 查表的列信息:
SELECT ... FROM sys_attribute WHERE attrelid = 'mytable'::regclass - 查索引信息:通过
sys_index关联sys_class - 查自定义类型:
SELECT oid, typname FROM sys_type WHERE typtype = 'c'
-- 直接把 OID 翻译成表名
test=# SELECT 16700::regclass;
regclass
----------
teachers
(1 row)
:: 和 CAST 功能等价,按需选用。
总结
OID、ROWID和自增主键各有定位:OID适合系统目录查询,ROWID擅长行级定位与范围过滤,自增主键是业务唯一性的基石。合理选型能提升后端架构的可维护性和查询效率。记住:业务主键不能省,行标识按需选。
OID 是 KES 系统内部的"编号系统",在系统表里全局唯一,配合 做元数据查询非常顺手。但它有 4 字节的天花板,普通表里又是局部的,不适合做业务主键。
ROWID 是 KES 自己搞出来的行标识方案——物理存储、自动建唯一索引、支持范围查询和排>序分组,定位一行数据比 OID 精准得多。
SERIAL / BIGSERIAL 则是业务主键的标准选择,配合 PRIMARY KEY 约束,适合绝大多数应>用场景。
三者各管一段,搞清楚谁该在什么场合出场,你就算是把金仓数据库的行标识机制真正吃透了。
default_with_oidsdefault_with_rowiddefault_with_oidsdefault_with_rowiddefault_with_oidsdefault_with_rowidregclass
浙公网安备 33010602011771号