正文开始——
一、引言
在数据库存储引擎中,行标识符是实现数据定位、元数据管理、事务并发、索引扫描的基础能力。KingbaseES 作为深度兼容 Oracle 的国产企业级数据库,对行标识体系做了针对性增强与重构:一方面保留并优化了 OID 对象标识符,用于系统表与数据库对象管理;另一方面自研实现了 ROWID 逻辑唯一标识,用于满足 Oracle 生态迁移、高速行访问、批量数据处理等场景。
KES 对 OID 做了全局 / 局部双重特性区分,对 ROWID 做了独立数据类型、自动索引、单调递增、GUC 参数管控的完整设计。两者不可同时生效,通过参数优先级实现互斥控制,形成了一套兼顾系统稳定性、生态兼容性、业务易用性的行标识体系。本文将从 OID 核心机制、ROWID 专属能力、存储选型对比、元数据访问、参数治理五大维度展开完整说明。
二、KES 中 OID 对象标识符机制详解
2.1 OID 基本定义与核心定位
OID 全称Object Identifier(对象标识符) ,是 KES 内部用于唯一标识数据库对象的 4 字节无符号整数伪列。它是数据库内核识别表、视图、序列、函数、数据类型、索引等对象的核心依据,也是绝大多数系统表的默认主键。
OID 具备以下基础特征:
- 伪列属性:OID 属于系统隐藏字段,使用
\d命令无法查看,普通表默认不生成。 - 生成规则:仅在建表指定
WITH OIDS或 GUC 参数default_with_oids=true时才会自动生成。 - 长度限制:固定 4 字节,最大值约 42.9 亿,计数器溢出后会循环复用,存在重复风险。
- 双重特性:系统表 OID 全局唯一分配,普通表 OID 表内局部自增,这是 KES 区别于 PostgreSQL 的关键设计。
在 KES 中,OID 的核心定位是内核元数据管理,而非业务数据的唯一标识,因此不建议在业务表中作为主键使用。
本文验证KingbaseES版本
-- 查看当前数据库版本
SELECT VERSION();
编辑
2.2 系统表 OID:全局唯一、跨对象连续分配
KES 系统表(如sys_class、sys_type、sys_proc、sys_attribute)的 OID 由数据库全局计数器统一分配,跨表、跨对象保持连续与唯一性,不会出现表内独立编号的情况。
我们可以通过以下 SQL 验证系统表 OID 的全局特性:
-- 查看系统表sys_type的总数据量
SELECT COUNT(*) FROM sys_type;
-- 查看OID大于1208的记录数量
SELECT COUNT(*) FROM sys_type WHERE oid > 1208;
-- 查看大编号段OID分布
SELECT oid FROM sys_type WHERE oid > 33990;
从查询结果可以看出,系统表 OID 并非从 1 开始连续分配,而是全局统一递增,不同对象共享同一计数器。进一步验证:创建新对象时,OID 在不同系统表间保持连续:
-- 创建自定义组合类型,查看sys_type中的OID
CREATE TYPE aatyp IS TABLE OF INT;
SELECT oid, typname FROM sys_type WHERE typname = 'aatyp';
-- 创建自定义函数,查看sys_proc中的OID
CREATE OR REPLACE FUNCTION func_test05(i INT) RETURN INT AS
DECLARE vv INT;
BEGIN RETURN 1; END;
/
SELECT oid, proname FROM sys_proc WHERE proname = 'func_test05';
上述测试中,类型与函数的 OID 连续递增,证明 OID 是数据库级全局资源。
2.3 普通表 OID:局部独立、表内自增(KES 核心差异)
KES 为了提升稳定性、降低计数器冲突风险,将普通表 OID 设计为表内局部独立自增,从根源避免业务表大量插入导致系统 OID 快速耗尽。
普通表 OID 默认不生成,可通过两种方式启用:
- 会话级设置
default_with_oids=true; - 建表语句显式指定
WITH OIDS。
验证测试如下:
-- 1. 默认创建表,无OID字段
CREATE TABLE tt5(id INT);
INSERT INTO tt5 VALUES(10);
SELECT oid, id FROM tt5; -- 报错:oid字段不存在
-- 2. 开启参数后创建表,自动携带隐藏OID
SET default_with_oids TO true;
CREATE TABLE tt6(id INT);
INSERT INTO tt6 VALUES(10);
SELECT oid FROM tt6; -- 返回1,表内自增
SELECT * FROM tt6; -- 隐藏列,默认不展示
-- 3. 关闭参数,显式指定WITH OIDS建表
SET default_with_oids TO false;
CREATE TABLE tt7(id INT) WITH OIDS;
INSERT INTO tt7 VALUES(10);
SELECT oid, id FROM tt7; -- 返回1,表内自增
测试表明:KES 普通表 OID 从 1 开始、表内独立递增,与系统表全局 OID 完全隔离,这是 KES 在存储引擎层的重要优化。
2.4 OID 别名 regclass:简化元数据查询效率
在 KES 中,regclass是 OID 的专属别名类型,专门用于表、索引、视图等对象的 OID 快速转换,可将表名字符串直接隐式转换为对应 OID,大幅简化系统表关联查询。
sys_class系统表存储所有表 / 索引 / TOAST 表的 OID 与名称,是 regclass 的核心依赖:
-- 查询以t开头的表对象及其OID
SELECT oid, relname FROM sys_class WHERE relname LIKE 't%';
-- OID反向解析表名
SELECT 16700::regclass;
-- 传统方式:关联查询表字段信息
SELECT attrelid, attname, atttypid, attlen, attnum, attnotnull
FROM sys_attribute
WHERE attrelid = (SELECT oid FROM sys_class WHERE relname = 'teachers');
-- regclass简化写法:无需子查询,直接转换
SELECT attrelid, attname, atttypid, attlen, attnum, attnotnull
FROM sys_attribute
WHERE attrelid = 'teachers'::regclass;
regclass本质等价于SELECT oid FROM sys_class WHERE relname=?,是 KES 内核提供的语法糖,广泛用于元数据监控、运维脚本、系统视图开发。
2.5 OID 使用约束与最佳实践
- OID 为 4 字节整型,计数器达到 42.9 亿后会循环,无法保证全局永久唯一。
- 单表可通过唯一索引避免 OID 重复,但大数据量下会严重降低插入性能。
- 业务表唯一标识优先使用
SERIAL或BIGSERIAL,超大规模表推荐BIGSERIAL。 - OID 仅用于内核系统表、元数据访问、数据库内部对象管理。
三、KES 独有 ROWID 机制:逻辑行唯一标识与 Oracle 兼容实现
3.1 ROWID 核心定位与设计目标
ROWID 是 KES自研增强的伪列机制,主要面向 Oracle 兼容迁移、高速数据行定位、批量 DML 优化、日志解析等场景。与 OID 不同,ROWID 并非物理地址,而是逻辑唯一标识,具备全局唯一性、单调递增、自动索引、可比较、可查询等完整能力。
KES ROWID 设计目标:
- 提供与 Oracle 语法一致的 ROWID 使用方式,降低迁移成本;
- 保证行记录全局唯一、长期稳定、不随 VACUUM / 修改而变化;
- 自动创建唯一 BTREE 索引,提升等值查询与范围查询性能;
- 通过 GUC 参数统一管控,支持全局开启与表级指定;
- 与 OID 互斥,避免伪列冲突与存储冗余。
3.2 ROWID 核心特性
- 隐藏伪列:默认不展示,可显式查询,不支持 INSERT/UPDATE/DELETE;
- 全局唯一:基于事务回卷次数、事务 XID、事务内序号复合生成;
- 单调递增:后插入行的 ROWID 一定大于先插入行;
- 自动索引:创建时自动生成唯一约束 + BTREE 索引;
- 编码格式:23 位 64 进制字符串(A-Z、a-z、0-9、+、/);
- 存储长度:变长存储,占用 4~18 字节;
- 参数互斥:与 OID 不可同时生效,ROWID 优先级更高。
3.3 ROWID 三种启用方式
方式 1:全局 GUC 参数default_with_rowid
开启后,所有新建表自动携带 ROWID 隐藏列,default_with_oids自动失效:
-- 查看参数状态
SHOW default_with_oids;
SHOW default_with_rowid;
-- 同时为true时,ROWID生效、OID失效
CREATE TABLE tt11(id INT);
INSERT INTO tt11 VALUES(10);
SELECT rowid, id FROM tt11; -- 可查询
SELECT oid, id FROM tt11; -- 报错不存在
方式 2:建表显式指定WITH ROWID
表级启用,灵活控制,不影响全局:
CREATE TABLE student(
sno INT,
name VARCHAR(10),
birthday DATE,
department VARCHAR(10),
sex VARCHAR(10)
) WITH ROWID;
-- 插入测试数据
INSERT INTO student VALUES(1, 'li', '2018-01-01', 'physics', 'boy');
INSERT INTO student VALUES(5, 'lu', '2018-01-02', 'chinese', 'boy');
INSERT INTO student VALUES(3, 'wang', '2018-01-03', 'english', 'girl');
INSERT INTO student VALUES(4, 'zhang', '2018-01-04', 'history', 'boy');
INSERT INTO student VALUES(2, 'jack', '2018-01-05', 'history', 'boy');
-- 查询ROWID
SELECT rowid, * FROM student;
-- 查看自动生成的唯一索引
\d student;
方式 3:已有表添加 ROWID
ALTER TABLE 表名 SET WITH ROWID;
3.4 ROWID 数据类型深度解析
ROWID 是 KES 内置独立数据类型,具备严格的格式约束:
- 显示长度:固定 23 位 64 进制字符串;
- 内部结构:32 位事务回卷次数 + 32 位事务 XID + 64 位事务内序号;
- 合法范围:最小
AAAAAAAAAAA,最大P//////////; - 比较规则:先比较回卷次数 → 事务 XID → 事务内序号;
- 支持运算符:=、>、>=、<、<=、!=,不支持算术运算。
类型验证与非法输入测试:
-- 创建ROWID类型表
CREATE TABLE rowid_tt1(id ROWID);
-- 合法ROWID导入导出
COPY rowid_tt1 FROM '/tmp/rowid_tt1.txt';
SELECT * FROM rowid_tt1;
COPY rowid_tt1 TO '/tmp/rowid_tt1_to.txt';
-- 非法长度:报错
INSERT INTO rowid_tt1 VALUES('AAAAAAAAAAABAAAAAAAAAAA4444');
INSERT INTO rowid_tt1 VALUES('AAAAAAAAAAABAAAAA');
-- 比较查询
SELECT * FROM rowid_tt1 WHERE id >= 'AAAAAAAAAAABAAAAAAAAAAB';
3.5 ROWID 使用规范与限制
- 支持在 SELECT 投影列、WHERE 条件、ORDER BY、GROUP BY 中使用;
- 支持存储过程、函数、动态 SQL 中使用;
- 支持 BTREE、HASH 索引;
- 不可参与算术运算、字符串拼接、函数运算;
- 不可手动更新、插入、删除;
- 全局参数与表级指定冲突时,以表级为准。
四、核心存储机制对比:OID vs ROWID vs 自增主键
在 KES 实际开发中,OID、ROWID、SERIAL/BIGSERIAL 自增主键是最常见的三种行标识方案,三者定位、能力、性能、适用场景差异显著,以下是全面对比:
| 对比维度 | OID | ROWID | SERIAL/BIGSERIAL |
| 类型 | 系统隐藏伪列 | 系统隐藏伪列 | 显式业务列 |
| 唯一性 | 系统表全局 / 普通表局部 | 全库逻辑唯一 | 表内唯一 |
| 自动索引 | 无,需手动创建 | 自动唯一 BTREE 索引 | 需手动建主键 / 索引 |
| 长度 | 固定 4 字节 | 变长 4~18 字节 | 4/8 字节 |
| 上限 | 42.9 亿,可循环 | 几乎无上限 | BIGSERIAL 近无限 |
| 优先级 | 低于 ROWID | 高于 OID | 业务最高 |
| 生态兼容 | PostgreSQL 原生 | Oracle 高度兼容 | 通用标准 |
| 性能开销 | 极低 | 低(带索引) | 低 |
| 适用场景 | 系统表、元数据、内核对象 | Oracle 迁移、高速行定位、批量 DML | 业务主键、表关联、分布式事务 |
选型结论:
- 内核运维、元数据访问 → 使用 OID;
- Oracle 迁移、快速行定位、兼容改造 → 使用 ROWID;
- 业务系统、表关联、外键、持久主键 → 使用 BIGSERIAL。
五、元数据访问与系统表解析
KES 的 OID 与 ROWID 均依赖系统表实现管理与查询,掌握系统表结构可大幅提升 DBA 运维效率:
- sys_class:所有表、索引、视图、序列的 OID 与基础信息;
- sys_type:数据类型、自定义类型的 OID 管理;
- sys_proc:函数、存储过程的 OID 管理;
- sys_attribute:表字段、系统隐藏字段(tableoid、ctid、xmin 等);
- sys_database:数据库对象 OID;
- sys_namespace:模式(Schema)OID。
结合 regclass 可实现高效元数据查询:
-- 查询表的全部字段(含系统隐藏列)
SELECT * FROM sys_attribute WHERE attrelid = 'teachers'::regclass;
六、兼容性与 GUC 参数冲突治理
OID 与 ROWID 的 GUC 参数存在严格互斥规则,是 KES 稳定运行的关键保障:
- 互斥规则:
default_with_oids与default_with_rowid不可同时生效; - 优先级:ROWID 参数优先级高于 OID;
- 冲突报错:OID 开启、ROWID 关闭时,执行
WITH ROWID建表直接报错; - 生效范围:全局参数影响后续新建表,表级指定仅作用于当前表。
冲突场景验证:
SET default_with_rowid TO false;
SET default_with_oids TO true;
CREATE TABLE tt16(id INT) WITH ROWID; -- 报错:无法创建
参数最佳实践:
- 新业务系统:全局开启 ROWID,关闭 OID;
- Oracle 迁移项目:强制使用 ROWID,禁用 OID;
- 混合环境:使用表级指定,避免全局冲突。
七、附录:类型转换符::与 CAST 用法
::是 KES 中的强制类型转换符,等价于标准 SQL 的CAST,广泛用于 OID、regclass、ROWID 等类型转换:
-- :: 简写方式
SELECT '25'::INTEGER, '12-oct-2023'::DATE, sys_typeof('25'::INTEGER);
-- CAST标准方式
SELECT CAST('25' AS INTEGER), CAST('12-oct-2023' AS DATE);
八、总结
KingbaseES 中的 OID 与 ROWID 构成了一套分层、互补、互斥的行标识体系。OID 专注于系统表与元数据管理,采用全局 + 局部双重分配策略,保证内核稳定;ROWID 作为 KES 独有增强能力,提供逻辑唯一、单调递增、自动索引、Oracle 兼容的行定位能力,完美支撑企业级迁移与高性能场景。
在实际工程应用中,必须明确三者边界:OID 用于系统、ROWID 用于兼容、自增主键用于业务,并通过 GUC 参数合理管控,避免冲突。理解 OID 与 ROWID 的底层实现、特性差异与使用规范,能够帮助开发者与 DBA 更好地利用 KES 内核能力,提升系统设计合理性、查询性能与迁移效率。
作为国产企业级数据库,KES 通过对 OID 的优化与 ROWID 的自研实现,既保持了开源生态的开放灵活,又满足了政企核心系统对兼容性、稳定性、高性能的刚性需求,是关键行业核心业务系统的理想选择。