当前位置: 首页 > article >正文

行标识符机制的技术演进与实践(上)——从OID说起

文章目录从对象标识说起——OID的前世今生OID到底是全局的还是局部的?OID的历史演进——从默认到逐步弃用regclass——OID的语法糖OID在系统表中的应用细节sys_class——数据库对象的户口本sys_type和sys_proc——类型和函数的OID管理OID与ctid的关系和区别OID与其他数据库主键机制对比Oracle的ROWIDMySQL的自增主键KES的双轨制设计OID的局限性——坑在哪里高并发场景下的性能问题从技术演进角度的思考兼容是对前人努力的尊重是确保业务平稳过渡的基石然而这仅仅是故事的起点说实话,在接触国产数据库之前,我一直没太在意过行标识符这回事。不就是主键嘛,建个ID字段不就完事了?结果后来有次做Oracle迁移项目,才被现实狠狠打脸。那个项目里大量存储过程都在用ROWID做游标定位,一查才知道Oracle的ROWID是物理地址,性能贼快,但迁移到其他数据库就懵了——完全不是一回事儿。后来接触KingbaseES(后面就简称KES吧),发现它搞了个OID加ROWID双轨制,挺有意思的,就花时间研究了一下。这篇文章主要聊聊OID,下篇再说ROWID。从对象标识说起——OID的前世今生要说OID这东西,得先从数据库对象管理说起。我记得刚入行那会儿,看到系统表里各种OID字段,还以为是什么高大上的东西。后来才知道,这玩意儿就是个4字节的无符号整数,PostgreSQL很早就引入了,专门给系统表用的。KES继承了这套东西,但在实现上又做了些调整,导致好多从PG转过来的人都会掉坑里——我也是其中一个。OID到底是全局的还是局部的?这个问题争议挺大的,我记得在某个技术群里还吵过一架。早期的PG文档里说OID是全局唯一的,在整个数据库实例里不会重复。但KES的R6版本改了逻辑,系统表的OID确实全局唯一,但普通表的OID变成了局部计数,每个表从1开始自己数。这个改动当时可把我坑惨了。看段代码就明白了:-- 先看系统表的OID,明显不是从1开始的test# select oid,typname from sys_type where oid 33990;oid|typname-------------------33992|aatyp33993|func_test0533995|...(3rows)-- 新建个类型和函数,OID连续递增cpbd_testcreatetypeaatypistableofint;CREATETYPEcpbd_testselectoid,typnamefromsys_typewheretypnameaatyp;oid|typname----------------55136|aatyp(1row)cpbd_testcreateorreplacefunctionfunc_test05(iint)returnintasbeginreturn1;end;CREATEFUNCTIONcpbd_testselectoid,pronamefromsys_procwherepronamefunc_test05;oid|proname--------------------55137|func_test05(1row)看到了吧,系统对象的OID是全局递增的,55136后面紧跟着55137。这个还算正常。但普通表就不一样了:-- 默认情况下普通表压根没有OIDtest# create table tt5(id int);CREATETABLEtest# insert into tt5 values(10);INSERT01test# select oid,id from tt5;ERROR:columnoiddoesnotexist-- 开启参数后才有,而且每个表从1开始test# set default_with_oids to true;SETtest# create table tt6(id int);CREATETABLEtest# insert into tt6 values(10);INSERT01test# select oid,id from tt6;oid|id---------1|10(1row)test# create table tt7(id int) with oids;CREATETABLEtest# insert into tt7 values(10);INSERT01test# select oid,id from tt7;oid|id---------1|10(1row)tt6和tt7的OID都从1开始,这就证明了普通表的OID是局部的。我当时看到这个结果,第一反应是这也行?。这个设计改动很多人不理解,觉得为什么不跟PG保持一致?其实仔细想想也有道理,全局OID意味着要维护一个全局计数器,高并发下会有性能瓶颈——这个应该算是权衡后的选择吧。改成局部计数,每个表自己管自己,并发性能好多了,就是失去了全局唯一性。说实话,我个人觉得这个改动有点可惜,毕竟全局唯一性在某些场景下还挺有用的。OID的历史演进——从默认到逐步弃用说起来,OID这个东西在PG的发展历程里也经历了不少变化。早期版本(8.0之前)所有用户表默认都带OID列,每行数据都有一个,可以直接用SELECT * FROM table WHERE oid 12345这种写法。那时候设计初衷是用OID来标识实例中所有数据库对象,包括用户数据表中的元组。但到了PG 8.0(2005年),情况就变了。官方默认不再给用户表添加oid列,想用的话得显式指定WITH OIDS。为啥要改呢?主要是几个问题:安全性问题——OID单调递增,攻击者可以通过观察OID值来推测数据量、插入时间这些敏感信息。我之前有个项目,用户就是通过观察OID发现了我们系统的真实数据量,搞得挺被动。存储开销——每行多4字节,看着不多,但对于宽表或者大数据量的表来说,这个开销就有点可观了。复制兼容性——在逻辑复制或者主从切换的时候,OID可能会出现重复的情况,这个坑我踩过好几次。再到后来,PG 12(2019年)干脆把WITH OIDS语法都给废弃了,用户表彻底不能用OID了。官方的考虑很明确:OID应该只用在系统表上,用户表还是用显式的主键或者SERIAL更靠谱。KES呢,为了兼容旧版本和Oracle迁移需求,保留了OID支持,还搞了个ROWID。这个双轨制的设计挺有意思的,但也带来了一些认知上的混乱——到底是该用OID还是ROWID?这个问题我们下篇再细说。regclass——OID的语法糖不过OID有个好处,就是可以用regclass类型简化查询。啥意思呢?看这个例子:-- 想查teachers表的字段信息,传统写法要关联sys_classtest# SELECT attrelid,attname,atttypidFROMsys_attributeWHEREattrelid(SELECToidFROMsys_classWHERErelnameteachers);-- 用regclass就简单多了test# SELECT attrelid,attname,atttypidFROMsys_attributeWHEREattrelidteachers::regclass;teachers::regclass这写法本质上就是select oid from sys_class where relnameteachers,但代码简洁多了,看着也舒服。KES里这种类型转换还挺常用的,特别是查系统表的时候。我之前写过一个元数据查询工具,用了不少这个语法糖,省了很多代码。实际上,regclass这类OID别名类型还有一些有趣的特性。比如当你在一个存储表达式(像列默认值或视图定义)中使用my_seq::regclass这样的常量时,系统会自动创建对该序列对象的依赖关系。这意味着你不能直接删除被引用的序列,除非先去掉这个依赖。这个机制挺智能的,避免了因为误删对象导致的一堆报错。OID在系统表中的应用细节既然OID主要是给系统表用的,那我们就来深入看看它在系统表里是怎么发挥作用的。sys_class——数据库对象的户口本sys_class是KES里最核心的系统表之一,记录了几乎所有的数据库对象(表、索引、视图、序列等)的元数据。它的oid字段就是这些对象的唯一标识。-- 查看所有表对象的OID和名称test# select oid,relname,relkind from sys_classwhererelkindrandrelnameliket%limit5;oid|relname|relkind-------------------------24903|tt1|r33139|tt5|r33346|table_t1|r33664|tbl_time|r33726|t1|r(5rows)这里有个小技巧:oid本身是数字,不太好记,但通过::regclass转换就能看到对象名称:test# select 16700::regclass;regclass----------teachers(1row)sys_type和sys_proc——类型和函数的OID管理类型系统和函数系统也重度依赖OID。每个数据类型、自定义类型、函数(包括重载版本)都有唯一的OID:-- 查看内置类型的OIDtest# select oid,typname from sys_type where typname in (int4,text,varchar);oid|typname----------------23|int425|text1043|varchar(3rows)-- 函数重载,OID不同test# create function my_func(int) returns int as select 1 language sql;CREATEFUNCTIONtest# create function my_func(text) returns text as select a language sql;CREATEFUNCTIONtest# select oid,proname from sys_proc where proname my_func;oid|proname----------------55140|my_func55141|my_func(2rows)这个OID机制支撑了PG/KES强大的扩展性和函数重载能力。函数执行计划缓存、权限控制、类型匹配等都依赖OID来实现。OID与ctid的关系和区别说到行标识符,除了OID,还有一个ctid(元组标识符)也很重要。ctid表示的是行的物理位置,格式是(块号,行号),比如(0,1)表示第0个数据块的第1行。OID和ctid有本质区别:OID是逻辑标识,系统对象全局唯一,普通表局部唯一ctid是物理地址,会随着VACUUM、UPDATE等操作改变test# select oid,ctid,* from tt6;oid|ctid|id----------------1|(0,1)|10(1row)-- 更新后ctid会变test# update tt6 set id 20 where id 10;UPDATE1test# select oid,ctid,* from tt6;oid|ctid|id----------------1|(0,2)|20(1row)可以看到,UPDATE后OID没变(还是1),但ctid从(0,1)变成了(0,2),因为PG/KES的UPDATE本质是标记旧行删除、插入新行。所以ctid不能作为长期引用的标识,只能在当前事务或短时间内使用。OID与其他数据库主键机制对比了解了OID的特点后,我们再看看其他数据库是怎么处理行标识的。Oracle的ROWIDOracle的ROWID是物理地址,包含文件号、块号、行号等信息,定位速度极快,可以直接跳过索引查找。但问题是:物理依赖性强——ROWID会在表移动、导出导入、分区重组等操作后改变不可跨数据库——只有Oracle有这个概念,其他数据库都没有索引组织表特殊——IOT表使用的是逻辑ROWID,基于主键编码我之前那个Oracle迁移项目,大量存储过程用ROWID做游标定位,迁移到KES后这些逻辑都得重写,因为ROWID机制完全不同。MySQL的自增主键MySQL(InnoDB)用的是聚簇索引,主键就是行的物理存储位置,没有单独的行标识符概念。这个设计其实挺简洁的:主键查询效率高(直接定位到数据页)节省存储空间(不需要额外的行ID字段)但必须显式定义主键,否则会用隐藏的6字节ROW_IDKES的双轨制设计KES比较特殊,既有OID(继承自PG),又引入了ROWID(兼容Oracle):OID:系统表专用,4字节,会回卷,不推荐业务表使用ROWID:逻辑标识,23字节字符串,单调递增,更适合业务场景这个设计既保持了PG兼容性,又照顾了Oracle迁移需求,但也增加了理解成本。OID的局限性——坑在哪里但OID也有坑,最大的问题就是会回卷。4字节整数最大值42亿,超过了就从头开始。虽然官方说建个唯一索引能防止重复,但你想啊,表数据要是上了十亿级别,每次插入都要检查OID是否重复,性能得多差?我之前有个项目,表里大概有5亿条数据,用OID的时候插入速度慢得要命,后来改成显式主键才解决。-- 官方建议:超过20亿条数据别用OID-- 要用bigserial或者显式主键test# create table big_table(id bigserialprimarykey,datatext);所以OID这东西,系统表里用用还行,业务表就别指望了。我记得有次跟同事讨论这个问题,他说那我用OID做业务主键不就行了?我说行是行,但你确定你的表永远不超过20亿条数据?他愣了一下,然后就默默改设计方案了。还有一个坑,就是OID在某些情况下会被重用。比如你删了一行数据,OID可能会被新的数据占用。这个在需要长期保存引用的场景下是个大问题,虽然可以通过加约束来避免,但总觉得不太靠谱。高并发场景下的性能问题全局OID虽然好理解,但在高并发场景下确实有性能瓶颈。我记得有个电商项目,日均插入量在千万级别,一开始用了OID(Oracle迁移过来的老代码),结果发现插入性能越来越差。排查后发现,问题出在OID分配上:全局OID计数器需要加锁高并发插入时,锁竞争严重虽然PG/KES有预分配机制(每次分配8192个OID),但仍然不够用后来改成了局部OID(每个表独立计数),性能好了不少,但也就失去了全局唯一性的优势。这就是个权衡取舍的问题。从技术演进角度的思考回顾OID的发展历程,其实能看出数据库设计理念的变化:早期(90年代):面向对象思潮盛行,OID被设计成所有对象的统一标识,寄希望于实现真正的对象数据库。中期(2000年代):实践发现OID在用户表上有诸多问题(安全性、性能、可移植性),开始逐步弱化,只保留在系统表层面。现代(2010年代后):随着分布式系统的发展,全局唯一ID的需求更加突出,但用的是UUID、Snowflake等更现代的方案,而不是4字节的OID。KES在这个演进过程中处于一个有趣的位置:既要保持PG兼容性(保留OID),又要支持Oracle迁移(引入ROWID),还要适应现代业务需求。这个平衡其实挺难把握的。我个人觉得,KES R6把普通表的OID改成局部计数是个务实的选择。虽然失去了全局唯一性,但在高并发场景下性能更好,而且现代应用本来就不太依赖OID做业务标识。系统表保留全局OID则保证了元数据管理的一致性,这个设计还算合理。下篇预告:既然OID有这么大的局限性,KES又是如何解决行标识符问题的呢?下一篇文章我们会深入探讨ROWID机制——这是KES为了兼容Oracle而设计的另一套行标识方案。说实话,ROWID的设计思路挺有意思的,跟Oracle的实现完全不一样,而且还有不少实战技巧。此外,还会介绍元数据访问、参数治理的坑,以及几个我实际遇到过的场景。敬请期待!

相关文章:

行标识符机制的技术演进与实践(上)——从OID说起

文章目录从对象标识说起——OID的前世今生OID到底是全局的还是局部的?OID的历史演进——从默认到逐步弃用regclass——OID的语法糖OID在系统表中的应用细节sys_class——数据库对象的"户口本"sys_type和sys_proc——类型和函数的OID管理OID与ctid的关系和区别OID与其…...

nuScenes 与 nuImages 对比分析:两大数据集工具使用全攻略

nuScenes 与 nuImages 对比分析:两大数据集工具使用全攻略 【免费下载链接】nuscenes-devkit The devkit of the nuScenes dataset. 项目地址: https://gitcode.com/gh_mirrors/nu/nuscenes-devkit nuScenes 与 nuImages 是自动驾驶领域的两大重要数据集&…...

【实践项目】 自定义服务消息Service(数学运算)

一、案例整体说明1. 功能需求实现一套 ROS 服务通信:客户端:发送两个数字 一个运算符( - * / %)服务端:接收数据并计算,返回结果自定义服务消息:约定请求 / 响应的数据格式2. 技术架构自定义服…...

open-vm-tools 开发入门:如何为开源虚拟化工具贡献代码

open-vm-tools 开发入门:如何为开源虚拟化工具贡献代码 【免费下载链接】open-vm-tools Official repository of VMware open-vm-tools project 项目地址: https://gitcode.com/gh_mirrors/op/open-vm-tools open-vm-tools 是 VMware 官方开源的虚拟化工具集…...

告别SPI瓶颈:用STM32的FSMC并行接口驱动LAN9252,榨干EtherCAT从站性能

突破EtherCAT从站性能极限:STM32 FSMC并行接口驱动LAN9252全解析 在工业自动化领域,实时以太网协议EtherCAT因其卓越的性能表现已成为运动控制系统的首选。然而许多工程师在实际部署中常遇到一个尴尬局面——主站协议处理速度飞快,而从站控制…...

Win11Debloat:解放你的Windows系统,5分钟焕新极简体验

Win11Debloat:解放你的Windows系统,5分钟焕新极简体验 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declu…...

moectf2025 rush

如此例题:利用随波逐流进行解题,将图片拖进去,在图片隐写中,找到GIF分离,将rush.gif分解为一张张独立的静态图片,并保存在新的文件夹中利用QR research进行扫描即可得出flag为moectf{QR_C0d3s_feATUR3_eRror_c0RRECt10N}...

快消企业如何选择品牌策划公司?

在寻找专业的品牌策划公司时,福建远见品牌策划公司凭借其系统的策略思维、创新的执行能力和显著的行业口碑,成为众多企业,尤其是注重品牌长期价值企业的优先选择。为什么选择专业的品牌策划公司?专业的品牌策划公司能帮助企业&…...

DeepSeek-OCR-2部署指南:Docker镜像开箱即用,无网络依赖保隐私

DeepSeek-OCR-2部署指南:Docker镜像开箱即用,无网络依赖保隐私 1. 项目简介 DeepSeek-OCR-2 是一个基于深度学习的智能文档解析工具,专门为解决文档数字化难题而设计。与传统的OCR工具只能提取纯文本不同,这个工具能够理解文档的…...

Tensorflow-101自编码器AE深度解析:降噪与卷积自编码器实现指南

Tensorflow-101自编码器AE深度解析:降噪与卷积自编码器实现指南 【免费下载链接】Tensorflow-101 项目地址: https://gitcode.com/gh_mirrors/te/Tensorflow-101 在深度学习领域中,自编码器(Autoencoder,AE)作…...

推荐系统必看:余弦距离与欧式距离在用户行为分析中的实战对比

推荐系统必看:余弦距离与欧式距离在用户行为分析中的实战对比 在构建推荐系统时,距离度量的选择往往决定了模型对用户偏好的理解深度。想象一下这样的场景:当两位用户同时观看了《星际穿越》和《盗梦空间》,但一位用户给前者打了…...

C++ 引入第三方库(三):使用 CMake 导入

使用 CMake 导入库其实应该称为:使用 CMake 将第三方库安装到 CMake 的 install 目录下。解释一下:首先,CMake 有着与 Maven 类似的 install 机制,通过 cmake --install 命令可以将本地项目安装到 CMAKE_INSTALL_PREFIX 目录下&am…...

FastAPI异步测试终极指南:从配置到实现的完整教程

FastAPI异步测试终极指南:从配置到实现的完整教程 【免费下载链接】fastapi FastAPI framework, high performance, easy to learn, fast to code, ready for production 项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi FastAPI异步测试是构建高…...

三步搞定B站视频下载:开源工具BiliDownload终极指南

三步搞定B站视频下载:开源工具BiliDownload终极指南 【免费下载链接】BiliDownload B站视频下载工具 项目地址: https://gitcode.com/gh_mirrors/bil/BiliDownload 在数字内容日益丰富的今天,B站(哔哩哔哩)已成为我们获取知…...

BeRoot代码实现原理:深入理解文件权限与服务配置检查机制

BeRoot代码实现原理:深入理解文件权限与服务配置检查机制 【免费下载链接】BeRoot Privilege Escalation Project - Windows / Linux / Mac 项目地址: https://gitcode.com/gh_mirrors/be/BeRoot BeRoot是一款强大的权限提升工具,支持Windows、Li…...

5步实战指南:深度解析UEFITool 0.28固件编辑工具的核心应用

5步实战指南:深度解析UEFITool 0.28固件编辑工具的核心应用 【免费下载链接】UEFITOOL28 项目地址: https://gitcode.com/gh_mirrors/ue/UEFITOOL28 UEFITool 0.28是一款专业的跨平台UEFI固件解析与编辑工具,采用C/Qt框架开发,支持Wi…...

梯度下降为什么总往‘下坡’走?用Python代码直观理解负梯度的奥秘

梯度下降为什么总往‘下坡’走?用Python代码直观理解负梯度的奥秘 想象你站在一座云雾缭绕的山丘上,手拿一张只能显示当前位置坡度的手绘地图。你的目标是找到下山最快的路径——这恰恰是梯度下降算法要解决的核心问题。对于机器学习初学者而言&#xff…...

好写作AI毕业论文功能实测:你的论文写作智能副驾已上线

写论文这件事,你负责开车,AI负责导航——分工明确才能到终点 想象一个场景:你刚拿到驾照,要独自开一趟1000公里的长途。 导航软件给你规划了路线,但你得自己看路标、自己踩油门、自己判断什么时候变道、自己找加油站。…...

TensorFlow Lite Micro入门教程:5分钟搭建你的第一个嵌入式AI应用

TensorFlow Lite Micro入门教程:5分钟搭建你的第一个嵌入式AI应用 【免费下载链接】tflite-micro Infrastructure to enable deployment of ML models to low-power resource-constrained embedded targets (including microcontrollers and digital signal process…...

从PyTorch到Android:YOLOv11模型轻量化部署与Qt实战避坑指南

1. 为什么选择Qt for Android部署YOLOv11? 对于习惯C开发的工程师来说,用Qt框架做Android端部署是个非常务实的选择。我去年接手一个农业巡检项目时,需要在无人机平板上实时检测作物病害,当时尝试过Android Studio方案&#xff0c…...

编码检测终极指南:告别乱码的批量字符集检测解决方案

编码检测终极指南:告别乱码的批量字符集检测解决方案 【免费下载链接】EncodingChecker A GUI tool that allows you to validate the text encoding of one or more files. Modified from https://encodingchecker.codeplex.com/ 项目地址: https://gitcode.com/…...

nsenter 实战技巧:如何绕过 cgroups 限制进行容器诊断

nsenter 实战技巧:如何绕过 cgroups 限制进行容器诊断 【免费下载链接】nsenter 项目地址: https://gitcode.com/gh_mirrors/ns/nsenter 在容器化部署中,nsenter 是一款强大的系统工具,它能够让用户直接进入正在运行的容器命名空间&a…...

如何永久保存微信聊天记录:WeChatMsg免费工具完全指南

如何永久保存微信聊天记录:WeChatMsg免费工具完全指南 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeCha…...

FastAPI OpenAPI文档:从基础配置到高级定制的完整指南

FastAPI OpenAPI文档:从基础配置到高级定制的完整指南 【免费下载链接】fastapi FastAPI framework, high performance, easy to learn, fast to code, ready for production 项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi 想要快速构建API并自…...

2026本科毕业论文工具 TOP10:从选题到答辩,AI 帮你一键通关

毕业季的论文焦虑,几乎是每个本科生逃不开的 “必修课”。选题卡壳、文献堆砌、格式返工、查重降重反复折腾…… 与其硬熬,不如找对工具。今天就给大家整理了10 款超实用的 AI 毕业论文写作工具,尤其是榜首的 Paperxie,堪称本科生…...

SEO_本地商家如何进行有效的SEO推广

SEO推广的基础:为什么本地商家需要SEO 在如今的数字化时代,互联网已经成为人们获取信息、购买商品和服务的重要途径。对于本地商家来说,如何在这个竞争激烈的市场中脱颖而出,是一个不容忽视的问题。这时,SEO推广应运而…...

别再只用WinForm了!用Godot 4.2给西门子PLC做个炫酷3D监控界面(附完整C#源码)

工业自动化新视界:用Godot 4.2打造PLC三维监控系统的实战指南 当传统工控界面遇上现代游戏引擎技术,会碰撞出怎样的火花?在工业4.0时代,设备监控系统早已不再满足于简单的二维图表和静态指示灯。想象一下:通过逼真的三…...

Go Context 控制流的正确使用方式

Go语言中的Context是控制并发流程的重要工具,它不仅能传递请求范围的数据,还能优雅地处理超时、取消等场景。正确使用Context可以避免资源泄漏、提升程序健壮性,但错误的使用方式可能导致难以排查的问题。本文将深入探讨Context的核心使用原则…...

URDF避坑指南:如何用SolidWorks导出模型并优化ROS仿真效果

URDF工业级建模实战:从SolidWorks到Gazebo仿真的全流程优化 在机器人开发领域,URDF(统一机器人描述格式)作为ROS生态中的标准建模语言,承担着连接机械设计与算法仿真的关键桥梁作用。然而,当开发者从基础UR…...

数据本体论 vs 数仓实体建模?

一、定义与起源 维度 数据本体论 (Data Ontology) 数仓实体建模 定义 哲学“存在论”在计算机领域的应用,强调语义统一 数据库ER建模方法,强调数据结构化与存储优化 核心思想 以“概念/类”为中心,描述事物“是什么”及“为何关联” 以“…...