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

电科金仓 OID 和 ROWID,这两天折腾迁移的一点碎碎念本

摘要:本文以电科金仓(KES)替换 Oracle 11g 的政务云迁移实战为背景,深入解析 OID 与 ROWID 的双轨架构:OID 是内核管理数据库对象的表级标识,不可用于业务主键;ROWID 是行级物理地址伪列,适用于增量同步断点续传、孤儿数据清洗及性能调优,但会因数据变更而改变,不能作为长期业务标识。文章通过 SQL 案例与踩坑经验,强调国产数据库迁移需摒弃 Oracle 思维定式,合理利用底层特性方能提升效率。

目录

1.先说说 OID,这玩意儿真别乱碰

2.ROWID:这才是行级寻址的硬通货

3.迁移实战:用 ROWID 搞定增量同步

4.数据清洗:用 ROWID 干掉"孤儿数据"

5.避坑指南:全是血泪史

6.性能调优的一些零碎经验

7.写在最后


本来这周打算把上个月攒下的绩效考评填完就休息一下,结果周一早上刚到工位,还没来得及泡第二杯咖啡,兄弟部门那个平时不太爱说话的开发组长老张就直接把我拉进了腾讯会议。屏幕共享一打开,满屏的 SQL 报错和一堆红色的告警邮件差点把我晃晕。背景是他们负责的一个省级政务云平台要做国产化替换,源端是那套跑了快十年的 Oracle 11g,目标端选的是电科金仓(KingbaseES,后面我都简称 KES)。数据量倒不是说大到离谱,核心交易表也就三千万出头,但业务逻辑那叫一个乱,各种历史遗留的触发器、存储过程,还有一堆我看着都头疼的动态 SQL。

老张一开口就是那个经典问题:“咱们 KES 里有没有类似 Oracle 那种 ROWID 的东西?我们现在的增量同步脚本全靠那个定位。”

我当时心里咯噔一下。说实话,这问题我听过太多次了。很多从 Oracle 转过来的团队,潜意识里总觉得“国产数据库肯定得兼容 Oracle 的那套玩法”,甚至有人觉得 ROWID 就是个通用的标准。但真要在企业级环境里玩转电科金仓,你得先搞清楚它底层的双轨架构——也就是OID​ 和ROWID​ 这两套东西到底怎么分工,谁管对象,谁管行,什么时候能用,什么时候用了就是给自己埋雷。

趁着这两天迁移告一段落,脑子里的细节还记得住,赶紧把这些坑和心得记下来。这篇东西我不打算写成那种教科书式的标准文档,太干了,后面接手的人估计看两页就关了。我就按咱们平时聊天的逻辑,想到哪说到哪,尽量把那些官方手册里没写的潜规则给扒出来。

1.先说说 OID,这玩意儿真别乱碰

咱们先从 OID(Object Identifier)说起。很多人第一次看电科金仓的系统表,比如sys_classsys_type这些,都会发现里面有个oid字段。我刚接触这数据库的时候也犯过傻,以为这玩意儿跟 Oracle 的 ROWID 差不多,是行的唯一标识,甚至还想拿它当业务主键用。后来被当时的架构师大佬指着鼻子骂了一顿,说这东西是给内核用的,不是给你业务用的。

OID 的本质是什么?它是电科金仓内核用来给数据库对象发身份证的。你建一张表,建一个索引,建一个视图,甚至是建一个序列,内核都会在系统表里给它登记一个全局唯一的整数 ID,这就是 OID。你可以随便找个测试库试试:

SELECT oid, relname, relkind FROM sys_class WHERE relname LIKE 't_order%';

这条语句查出来的oid,就是那张表在数据库字典里的编号。注意啊,这是表级别的,不是行级别的。也就是说,一张表不管有一万行还是一亿行,它的 OID 只有一个。

这里有个坑得重点提一下。老版本的 KES(具体版本号我记不清了,大概是 V7 或者更早的某些分支)在建表的时候,默认行为是WITH OIDS。这意味着如果你不显式声明,每行数据都会多出一个隐藏的oid字段。那时候确实有开发拿着这个当行标识用。但现在的新版本(V8 及以后),默认是关闭这个特性的。为什么要关?原因很简单,也很致命:

第一,性能问题。OID 字段默认是没有索引的。你拿一个没索引的整数去几百万、几千万的表里查,那就是全表扫描,慢得你想砸键盘。

第二,回卷风险。OID 是 32 位的,最大值大概是 42 亿。听起来很多?但在一个 7x24 小时运行的系统里,创建对象、临时表、索引,OID 是会一直涨的。万一哪天真回卷了(Wrap Around),系统直接给你来个逻辑损坏,到时候哭都来不及。

第三,迁移麻烦。你现在用 OID 当主键,万一哪天要迁到别的数据库,或者升级大版本,OID 不一定能保得住。

所以我现在跟团队定规矩:OID 只能在写运维脚本、查系统元数据的时候用,业务代码里要是敢出现SELECT ... WHERE oid = ...,直接打回重改。​ 它就是个后台管理员,别让它跑到前台来接客。

2.ROWID:这才是行级寻址的硬通货

说完 OID,再聊回老张关心的那个 ROWID。在电科金仓里,真正跟 Oracle 的 ROWID 对标的,就是这个伪列(Pseudo Column)。

这东西很有意思。它不是你建表的时候定义在 DDL 里的,也不是一个普通的字段,它是内核在查询的时候动态算出来的物理地址编码你可以随便找张表试一下:

SELECT ROWID, * FROM t_user LIMIT 10;

出来的那串字符,长得有点像 Base64 编码,看着乱七八糟,但其实里面藏着这行数据在硬盘上的精确坐标。虽然不同版本的内核实现细节可能有微调,但逻辑上基本都包含这几块信息:对象号(对应表 OID)、文件号、块号(Block Number)、行号(Slot Number)。

翻译成人话就是:哪个数据文件、第几个块、块里的第几行

为什么 DBA 都喜欢这东西?因为它快,快到不讲道理。

想象一下,你有一张 5000 万行的大表,上面只有一个主键索引,而且你的查询条件刚好没用上这个索引。正常情况下,数据库得把 5000 万行数据全部读一遍,过滤出符合条件的结果。但如果这时候你知道你要找的那行数据的 ROWID,数据库根本不用扫索引,也不用扫全表,直接拿这个地址去算文件偏移量,去硬盘上那个位置把数据抠出来就行。这叫物理寻址(Direct Access),速度差几个数量级。

上次我们有个财务结算的脚本,要更新一张大表里几万行状态异常的数据。开发写的 SQL 是按条件UPDATE,没走索引,跑了 40 分钟还没完。我过去看了一眼,直接给他改成两步:第一步先按条件把 ROWID 查出来存到临时表,第二步根据 ROWID 去更新。最后总共花了不到 3 分钟。老张当时看我的眼神都变了,说这也行?

3.迁移实战:用 ROWID 搞定增量同步

回到这次迁移。老张他们的痛点在于,源库 Oracle 的数据一直在写,不能停业务。所以他们想搞增量同步,也就是第一次全量迁完,后面每隔几分钟抓一次变化的数据。

在 Oracle 那边,他们原来的方案是利用ORA_ROWSCN或者ROWID范围。到了 KES,我们商量了一下,决定用 ROWID 做切片。

具体怎么做呢?我们写了一个脚本,先去源库(Oracle)里,按 ROWID 把表的数据切成一百份。怎么切?利用DBMS_ROWID包,或者干脆按ROWID的字符串前缀分桶。每一份大概几十万行。

然后,我们用 Kettle(现在叫 Pentaho)或者自己写的 Java 程序,把这几百个分片并发往 KES 里灌。中间出了几次岔子,比如网络抖动、目标库锁表,导致导到一半失败了。这时候 ROWID 的优势就体现出来了:我不需要回滚,也不需要重新导整个表,我只需要在日志里记下最后成功导入的那个 ROWID 分片的起点,下次从那个点接着往后导就行。

这就叫断点续传。如果你用LIMIT OFFSET来做分页迁移,OFFSET 大到一定程度,性能会断崖式下跌,而且中断了很难精准续上。

这里有个细节得注意。电科金仓的 ROWID 编码规则和 Oracle 是不一样的。你不能指望 Oracle 导出来的 ROWID 字符串直接插到 KES 里还能用。我们的做法是,在源端按 ROWID 分片是为了控制导出粒度,在目标端(KES)侧,我们是用自增的时间戳或者序列来做的幂等校验。别想着直接复用 ROWID 的值,那是给自己挖坑。

4.数据清洗:用 ROWID 干掉"孤儿数据"

迁移过程中,数据清洗也是个大头。老系统里,很多表当初建的时候没加外键约束,或者加了后来又给禁用了。结果就是,子表引用了父表的数据,父表的数据删了,子表里剩下一堆指向空值的"孤儿"。

比如有个t_order_detail表,里面的order_id理论上应该在t_order表里存在。但实际情况是,一查发现有十几万行order_idt_order里找不到。

开发的第一反应是用NOT IN

DELETE FROM t_order_detail WHERE order_id NOT IN (SELECT id FROM t_order);

这条 SQL 在小表上没问题,但在几千万的大表上,子查询先扫一遍t_order,然后外层再扫一遍t_order_detail,还要做 Hash 反连接,慢得离谱。我们当时跑了俩小时都没动静。

我给的方案是改成用 ROWID 关联删除:

DELETE FROM t_order_detail WHERE ROWID IN ( SELECT d.ROWID FROM t_order_detail d LEFT JOIN t_order o ON d.order_id = o.id WHERE o.id IS NULL );

逻辑是一样的,都是找孤儿。但区别在于,内层查询虽然还是要 JOIN,但最后返回的是t_order_detail的 ROWID。外层 DELETE 的时候,数据库直接拿着这些 ROWID 去物理定位行,不需要再回表查一次主键或者整行数据,I/O 压力小很多。那天改完之后,十几万行数据,大概几十秒就清完了。

5.避坑指南:全是血泪史

写了这么多实战,也得泼点冷水。ROWID 虽然好用,但它有个最大的特点:它会变

什么时候会变?

  1. 表重组(Alter Table Move):如果你对表做了ALTER TABLE ... MOVE TABLESPACE,或者做了在线重定义,数据的物理位置变了,ROWID 必变。

  2. 行迁移(Row Migration):如果你更新了一行数据,导致这行数据变大了,原来的数据块放不下了,数据库会把这行挪到新的块里。这时候 ROWID 也会变。

  3. 导出再导入:不管是逻辑导出(sys_dump)还是物理备份恢复,只要数据重新写过,ROWID 大概率会变。

所以,有几个铁律我必须强调:

  • 绝对不要把 ROWID 存到业务表里当永久关联键。​ 这跟自杀没区别。今天你存的 ROWID,明天表一移动,全废。

  • 不要用 ROWID 做分页查询。​ 网上有些所谓的"优化秘籍"教人用 ROWID 做分页,比如WHERE ROWID > ...。在 KES 里,除非你非常确定数据不会变,否则千万别这么干。分页结果会乱套的。

  • 注意兼容模式。​ 电科金仓安装的时候可以选择兼容模式(Oracle、PostgreSQL 等)。如果你选的不是 Oracle 兼容模式,可能压根不支持ROWID这个伪列。建库前一定要规划好,后期改模式比迁数据还痛苦。

6.性能调优的一些零碎经验

除了上面那些,平时运维 KES 的时候,针对 OID 和 ROWID,我还有几个习惯性的操作:

  1. 监控 OID 消耗。​ 虽然现在新版本 OID 回卷的概率低了,但作为 DBA,我还是习惯定期查一下sys_class里 OID 的最大值,看看增长趋势。特别是那种频繁建临时表的应用,OID 消耗很快。

  2. 批量处理用 ROWID 分桶。​ 如果你要对大表做全表扫描类的批量处理(比如算个复杂的统计),可以按 ROWID 把数据分成若干份,开多线程并发处理。因为 ROWID 代表了物理分布,分桶后的数据在磁盘上相对连续,能减少随机 I/O。

  3. 留意执行计划里的 ROWID 字样。​ 有时候你看到执行计划里有Bitmap Heap Scan或者Table Scan,后面跟着Filter: (ROWID ...),说明优化器在用 ROWID 做过滤。这时候要看一下代价是否合理,有时候统计信息不准会导致优化器误判。

7.写在最后

OID 和 ROWID 这套双轨架构,其实是电科金仓在企业级场景下做的一种权衡。OID 保证了内核管理对象的效率和稳定性,ROWID 提供了极致性能的物理寻址能力。对于开发人员和 DBA 来说,理解这两者的边界至关重要。

别总想着拿数据库底层的特性去炫技。OID 就老老实实待在系统表里,ROWID 就在你救火、迁数据、清脏数据的时候拿出来用用。业务代码里,该建主键建主键,该建索引建索引,别老想着走捷径。

这次迁移搞完,我最大的感触是:数据库这东西,越是底层的东西越危险。用好了是神器,用不好就是定时炸弹。特别是现在信创环境下,大家都在学国产数据库,千万别拿以前 Oracle 的经验生搬硬套。多看看执行计划,多测测性能,少踩点坑,咱们早点下班回家陪老婆孩子不香吗?

行了,就写到这。明天还得去给那帮开发做培训,讲讲怎么写规范的 SQL。希望能帮到他们吧,至少别再半夜给我打电话喊救命了。


金仓社区 - 电科金仓官方技术社区

10 个关键字解说

序号关键字解说
1OID内核给数据库对象(表、索引等)分配的全局唯一整数 ID,表级标识,严禁用于业务主键。
2ROWID内核动态计算的行物理地址伪列,包含文件号、块号、行号,可实现物理寻址直接访问。
3双轨架构KES 中 OID 管对象、ROWID 管行级的两套独立标识体系,分工明确、不可混用。
4物理寻址通过 ROWID 直接计算磁盘偏移量读取数据,绕过索引与全表扫描,性能提升数个数量级。
5回卷(Wrap Around)32 位 OID 达 42 亿上限后溢出回绕,导致对象标识冲突,是禁用 OID 于业务的核心原因。
6增量同步全量迁移后持续捕获源库变更数据,本文用 Oracle ROWID 分片实现不停机迁移。
7断点续传利用 ROWID 分片记录成功起点,中断后精准续传,避免 LIMIT OFFSET 性能断崖问题。
8孤儿数据子表外键在父表中找不到对应记录的悬空数据,本文用 ROWID 关联删除替代 NOT IN 优化性能。
9全表扫描无索引条件下逐行读取整张表的 I/O 操作,OID 无索引必触发此问题,代价极高。
10信创/国产化替换将 Oracle 等国外数据库替换为国产 KES 的过程,需重新理解底层特性而非生搬硬套。

免责声明:本文纯属个人工作笔记整理,不代表公司官方观点。文中涉及的 SQL 示例仅供参考,生产环境操作前请务必在测试环境充分验证。

http://www.jsqmd.com/news/1091971/

相关文章:

  • VEML7700驱动实战:从寄存器配置到光照数据采集
  • 任务依赖图解析:DAG的声明式编排与自动并行化
  • Whois域名查询API集成指南:从零搭建域名信息查询工具
  • 代码重构中的坏味道识别重构时机与方法选择
  • 必火AI数字人|全链路AI数字内容创作平台,产品全方位介绍
  • [经验分享] 我的第一个 Skill
  • VIM效率跃迁指南:基于coc.nvim构建现代化智能补全环境
  • QModMaster终极指南:如何用免费开源工具轻松调试ModBus设备
  • 道歉声明登报怎么办理?办理道歉声明登报需要哪些材料?
  • 2026TypeScript前端高频面试题总结大全(最新版)
  • 3步彻底卸载OneDrive:让你的Windows系统重获新生
  • R3nzSkin深度解析:游戏客户端内存操作技术的创新实践指南
  • 深度探索Ryujinx:用C构建的Nintendo Switch模拟器技术奥秘
  • TI TUSB系列芯片EEPROM在线编程:原理、工具与量产实战指南
  • CVE-2020-1938幽灵猫漏洞:AJP协议文件读取与代码执行深度剖析
  • 终极音乐解锁指南:如何在浏览器中自由转换加密音乐文件
  • 深入浅出 Linux 进程间通信:从匿名管道到内核 System V 对象
  • 终极防撤回解决方案:让微信QQ消息永久可见的完整指南
  • 终极指南:如何用Fan Control彻底解决Windows风扇噪音问题
  • 百度文库文档免费获取工具:127行代码实现高效自动化解决方案
  • ​2026海外五大社媒红人营销指南:分销转化与KOL营销潜力对比
  • 鸿蒙原生 ArkTS 布局深度解析:RelativeContainer 与宽高比控制实战
  • 问卷系统测试报告
  • MSP430X寄存器操作与寻址模式深度解析:嵌入式底层开发核心机制
  • AI辅助渗透测试实战:基于Gemini CLI的提示词设计与自动化应用
  • 零基础 Vibe Coding 教程 AI 编程的完整流程 33-36
  • [智能体-586]:OpenClaw(小龙虾) Hermes Agent 全量注意事项与潜在坑
  • Go语言的sync.RWMutex中的使用内存屏障
  • CDS API终极指南:3步解锁全球气象数据的Python实战教程
  • ChatGPT Plus / Pro 使用心得整理:真正拉开差距的,不是版本,而是用法