ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Oracle零改造迁移金仓:JSON行为与事务隔离深度解析

Oracle零改造迁移金仓:JSON行为与事务隔离深度解析 前几天做了一次从Oracle到金仓KingbaseES的迁移验证业务方给的KPI很直接应用代码一行不改数据库直接切。这种诉求这几年越来越常见也就是数据库圈子里经常提的“零改造”迁移。零改造不是把数据倒过去就算完而是要求目标库在内核层面把源库的行为尽量复刻出来——语法能解析只是最外层真正见功夫的是JSON行为差异、事务隔离语义这类细节。这次我把金仓的Oracle兼容能力从JSON函数一路测到事务隔离微调顺带把内核层面的实现机制也拆了一遍这篇东西就是给准备做Oracle迁移、或者正被JSON路径查询和并发报错折腾的朋友一份可以直接参考的经验。1. 零改造迁移的底层逻辑兼容性不是开关是内核能力1.1 “零改造”到底改了什么零改造迁移业界通常指应用层不修改或极少修改的情况下完成数据库替换。对从Oracle迁到金仓这类场景重点从来不是数据搬运而是SQL语义、存储过程、系统视图、事务行为、错误码映射这些软东西的整体对齐。为什么Oracle迁移历史上总是伴随“大改造”因为两个数据库的语法和语义差异太大了。空字符串和NULL是一个差异SYSDATE和NOW()是一个差异ROWNUM分页是一个差异JSON路径表达式又是一个差异。单个看都是小问题但业务系统里有几百张表、几千条SQL、几十个存储过程这些小差异叠加起来就是一场灾难。零改造能做出来本质上是目标数据库把兼容性做到了内核里而不是停留在文档层面的“支持Oracle语法”。这也解释了为什么金仓这类数据库要花大力气在内核里做Oracle兼容模式只有内核认识这些方言应用层的JDBC、ORM、存储过程才能原封不动地跑起来。1.2 内核做兼容比中间件改写靠谱在哪数据库迁移圈里一直有两条技术路线一条是用中间件或网关做SQL改写把Oracle SQL翻译成目标库方言另一条是目标库内核直接支持Oracle语法和语义。金仓走的是后一条也就是所谓的内核黑科技路线。中间件改写的路线看着轻巧实际上有几个绕不过去的坎。第一动态SQL怎么改业务代码里拼接出来的SQL中间件在运行时看到的是最终文本它得实时识别Oracle方言并完成改写复杂查询稍微带点嵌套子查询、分析函数、CONNECT BY改写逻辑就会变得非常脆弱。第二存储过程和包怎么办中间件只能处理SQL文本PL/SQL里的游标、异常、包变量这些控制流它根本碰不了。第三执行计划带不来。改写后的SQL在目标库生成的执行计划和DBA在Oracle里调优过的执行计划完全是两回事性能很容易劣化。金仓的做法是在内核的解析器、优化器、执行器里直接做分支处理。开启Oracle兼容模式后同样是空字符串赋值、同样是JSON_VALUE路径查询、同样是SET TRANSACTION ISOLATION LEVEL走的代码路径会切到Oracle语义分支而不是靠外挂转换。这样做代价是内核复杂度高但换来的是行为一致性和性能可控性对于真正要扛生产业务的系统来说这条路更稳。1.3 为什么单靠语法兼容不够语法兼容解决的是“这句话能不能解析”语义兼容解决的是“这句话跑出来的结果对不对”。零改造迁移最容易被忽视的就是语义层。举几个我实际遇到过的例子。Oracle里空字符串就等价于NULL但PostgreSQL系数据库里空字符串是空字符串NULL是NULL两者完全不同。你写WHERE col 在Oracle里永远查不到任何一行但换到PostgreSQL方言的库里就能查到一堆“空串”数据这个差异会让报表结果直接变样。再比如TO_DATE(2024-01-01)Oracle默认格式是DD-MON-RR还是YYYY-MM-DD取决于NLS参数而金仓在Oracle兼容模式下要把默认格式、隐式转换规则一起调对否则日期解析就会报错。JSON也是一样的道理。Oracle的JSON_VALUE用$.a.b这种路径语法而原生PostgreSQL习惯用-、-操作符。如果应用里写的是JSON_VALUE(doc, $.user.name)目标库不认识这个函数或者路径语义对不上那就不是“改造一行SQL”的问题而是整个查询模块都要重写。所以我把兼容性分成三层语法层、语义层、性能层。语法层是能不能解析语义层是结果对不对性能层是跑得快不快。金仓在内核里做的兼容是三层一起做这也是“零改造”能成立的前提。2. JSON行为差异两条技术路线的碰撞2.1 先从JSON的存储和返回形态说起JSON在Oracle和PostgreSQL系数据库里的底层实现完全不一样。Oracle的JSON从12c开始逐步完善早期是VARCHAR2、CLOB、BLOB列加IS JSON约束后来引入原生的JSON类型内部用OSON二进制格式存储。PostgreSQL系则是传统的json文本原样保存和jsonb二进制分解存储会排序键、去重键、重写格式两种类型。金仓做Oracle兼容JSON这块不能简单地把Oracle的JSON函数映射到jsonb就完事因为jsonb的行为和Oracle原生JSON的行为有很明显的差异。最典型的例子是键序jsonb在存储时会自动做键的排序重复键只保留最后一个输出格式会规范化Oracle虽然内部也用二进制格式但不会承诺键顺序的一致性。如果应用里有“把JSON文本当作签名串”“对JSON文本做全文比较”之类的逻辑从Oracle迁到基于jsonb模式的目标库输出的文本排版变了下游程序做MD5比对或者精确匹配就会失败。再一个是数字精度。Oracle的NUMBER类型精度很高能表达非常大的整数和非常精确的小数jsonb中的数字在PostgreSQL里走numeric精度和舍入规则与Oracle不完全一致。我测过一个场景JSON里有个字段存的是科学计数法的大数Oracle返回的是完整展开金仓在某些模式下返回的是规范化写法两边的字符串就不一样了。这里还要提醒一下全链路的场景。现在很多业务不是只有数据库前面还挂着Spark、Flink、Kafka后面还要被数据平台定期读走。用户在Spark中读取JSON是常规操作它关心的不是你能不能打开json文件而是数据在路径解析、类型推断、空值语义上的稳定性。数据库返回的JSON一旦在键序、转义、数字写法上有变化Spark解析出来的Schema可能就从structname:string变成了structNAME:string整条链路都得跟着调。2.2 JSON函数的行为差异最容易写错的地方Oracle的JSON函数体系很完整常用的有JSON_VALUE、JSON_QUERY、JSON_TABLE、JSON_EXISTS、IS JSON、JSON_OBJECT、JSON_ARRAY等。金仓在Oracle兼容模式下一个一个做了对齐但细节上还是得验证。函数或能力Oracle的习惯用法金仓Oracle兼容模式下要注意的点JSON_VALUEjson_value(doc, $.a.b)返回标量路径不存在时默认返回NULL还是报错取决于ERROR ON ERROR / NULL ON ERRORJSON_QUERY返回JSON片段可加WITH WRAPPER和JSON_VALUE的边界容易混取对象用JSON_QUERY取标量用JSON_VALUEJSON_TABLENESTED PATH展开数组列序、嵌套层级、NULL行的处理需要对照测试IS JSON校验字段是否为合法JSON迁移后约束是否保留取决于DDL迁移工具的处理JSON_OBJECT / JSON_ARRAY在SQL里构造JSON键名大小写、空值过滤规则在不同版本有微妙差异路径语法$开头字段名用双引号特殊字符、数组下标、通配符的写法要逐条验证路径查询是最容易出问题的。Oracle的JSON路径表达式里字段名默认大小写敏感如果要访问带特殊字符的键名得用双引号包起来比如$. user-name。金仓的兼容实现里如果底层走的是类JSQuery路径转义规则和Unicode处理都会有细微差异。JSON_VALUE和JSON_QUERY的边界问题也值得单独说。JSON_VALUE是取标量值返回的是SQL标量类型JSON_QUERY是取JSON片段返回的是JSON文档。很多新手在取一个嵌套对象时用了JSON_VALUE结果返回NULL或者报错因为在Oracle语义里“取对象”不是标量路径。金仓兼容模式下如果跟着Oracle语义走这个问题同样会出现所以不是数据库的问题而是应用写法本身要按Oracle规范来。JSON_TABLE是JSON数组转关系表的利器。业务里经常有这种需求订单表里存了一个商品数组要把它展开成一行一个商品的明细表。Oracle的写法是SELECT t.order_id, pt.item_name, pt.item_price FROM orders t, JSON_TABLE(t.ext_info, $.items[*] COLUMNS ( item_name VARCHAR2(100) PATH $.name, item_price NUMBER PATH $.price ) ) pt;金仓在Oracle兼容模式下要能接受这种写法同时要保证数组为空时的行为一致Oracle的JSON_TABLE在路径匹配不到时会返回零行这个语义如果实现偏差关联查询的结果集就会多出或者少掉一批数据。2.3 JSON迁移踩坑速记我这次迁移验证里JSON相关的坑主要集中在几个地方。一是存量数据迁移后的类型映射。原库是CLOB类型存JSON迁到金仓后如果变成jsonb或者自定义JSON类型应用原来用substr()、length()处理CLOB的代码就可能行为变化。需要提前确认迁移工具的类型映射规则必要时在迁移配置里指定目标类型。二是IS JSON约束的保留。Oracle里很多表的JSON列是用CHECK (col IS JSON)来约束的迁移DDL如果只建了列、丢了约束后续脏数据进来不会报错线上数据质量和原来就不一致了。三是索引问题。Oracle里可以对JSON列建函数索引来加速路径查询比如CREATE INDEX idx ON t (json_value(doc, $.a.b));。金仓迁移后如果这个索引没建上查询还是能跑但性能会差一个量级容易在压测阶段暴露。四是JSON数组和JSON格式文件的直觉差异。有些业务的数据流是先导出成json格式文件再通过程序批量加载。数据库侧的JSON数组格式如果是带空格、带Unicode转义的和文件里的排版对不上下游加载就会报解析错误。这一点在迁移验证时要专门准备几个带特殊字符、中文、空值、嵌套数组的用例不要只用标准小json测试。3. 事务隔离微调从快照语义到锁行为逐项对标3.1 读已提交一个SQL语句里到底看见什么Oracle和PostgreSQL系数据库默认隔离级别都是READ COMMITTED但两者的实现机制并不相同。Oracle靠undo段构造语句级的一致性读一个SELECT开始时会生成一个快照整个语句执行期间都看这个快照不受其他会话后续提交的影响PostgreSQL系的MVCC靠元组多版本每个语句开始时会取一个事务快照逻辑上也是语句级一致性。听起来差不多但细节差异会冒出来。比如一条UPDATE语句Oracle里是当前读UPDATE执行时会读取已提交的最新版本PostgreSQL的READ COMMITTED下UPDATE也有类似行为并发冲突时会等待并重新评估最新行版本。差异主要集中在长时间的游标操作和锁等待场景里Oracle的游标可以保持语句级快照而PostgreSQL系的游标在事务结束前每个FETCH都可能看到新的已提交数据。金仓在Oracle兼容模式下要做的是把这些语义尽可能往Oracle方向拉同时在参数层面允许微调。比如调整快照的获取时机、控制游标行为让应用感知尽量一致。对大多数OLTP业务来说默认的READ COMMITTED行为跑出来结果是一致的但如果你有那种事务很长、一个游标循环处理几万行的批处理就要重点验证。3.2 可重复读与串行化隔离级别映射不是翻译名词Oracle的标准隔离级别只有READ COMMITTED和SERIALIZABLE没有独立的REPEATABLE READ。PostgreSQL系则有READ COMMITTED、REPEATABLE READ、SERIALIZABLE三档。金仓在Oracle兼容模式下要处理两边的映射关系应用显式设置SET TRANSACTION ISOLATION LEVEL SERIALIZABLE时行为要和Oracle的SERIALIZABLE尽量一致如果应用写的是REPEATABLE READ也不能简单忽略。Oracle的SERIALIZABLE事务有一个经典报错ORA-08177意思是“无法串行化访问”。金仓这边如果底层是PostgreSQL的SSI实现对应的报错是could not serialize access due to concurrent update。应用层如果依赖错误码和错误消息做重试判断这里就要做映射。我在验证时专门构造过两个会话交替更新的场景目的就是看目标库报的错是不是能让应用正确识别。隔离级别Oracle行为金仓/PG系行为迁移注意点READ COMMITTED默认语句级一致性读默认MVCC快照大多数场景行为一致重点观察游标和长事务REPEATABLE READ不独立存在语义接近SERIALIZABLE有独立级别事务内快照一致显式指定的应用要对照验证SERIALIZABLE冲突报ORA-08177SSI实现报serialization failure错误码和重试逻辑要调整3.3 锁等待、死锁与并发压测里的微调事务隔离微调的重头戏在锁行为。我压测时发现同样一个并发更新场景Oracle里可能排队锁等待金仓这边可能更早报死锁或者deadlock timeout。原因不一定是目标库的死锁概率更高而是锁顺序、检测时机、等待上限不同。金仓有对应的锁等待、死锁检测参数可以调整。常见的几个方向和原则是把lock_timeout这类参数的控制权明确下来不要让应用拿默认值硬扛对高频更新的表建议在批处理里加上“按主键排序后更新”的约束让所有会话按统一顺序拿锁能大幅降低死锁概率死锁检测周期可以微调但不要为了“兼容Oracle”把检测时间拉得很长否则出了问题没人发现。锁等待超时是生产环境最常见的故障之一。Oracle里很多应用会设置wait或者nowait金仓这边要支持等价的语义。我踩过的坑是连接池里的初始化SQL有的连接池在获取连接时会执行SET TRANSACTION ISOLATION LEVEL READ COMMITTED这个语句在事务中间执行会直接报错导致连接建立失败需要排查的是连接池配置而不是数据库。3.4 事务隔离微调的边界别把内核参数当万能药零改造迁移要求目标库在行为上靠近源库但事务隔离的微调是有边界的。把参数调得太激进可能让一个业务正常了另一个业务反而出问题。比如把隔离级别默认值改成SERIALIZABLE能解决一部分并发写入的异常但会带来大量的序列化冲突重试性能直接下滑。我的建议是微调之前先明确三个问题。第一这个参数影响的是哪个具体业务场景能不能用压测数据证明现状和Oracle有差异第二改动之后对其他场景的副作用是什么至少要做一轮全量回归第三有没有不改参数、改SQL就能解决问题的方案。零改造不等于不能改SQL能通过等价改写解决的行为差异往往比调内核参数更可控。4. 内核层面的兼容性实现黑科技是怎么落地的4.1 兼容模式不是配置文件里加一行很多人在网上搜“金仓”相关的资料时会看到类似“切换兼容模式”的说法以为就是一个开关。实际上金仓的Oracle兼容模式是在内核层面做了大量分支处理的同一个解析器见到Oracle风格的SQL就走Oracle语义分支同一个事务管理器在Oracle模式下走对应的快照和锁语义。这也是为什么金仓能做到“permission should be urwx”这类权限问题彻底透明化——不这个是另一个问题了后面在排查章节单独说。我想强调的是兼容模式的背后是一整套内核改造包括语法解析分支、隐式类型转换、系统函数映射、视图与数据字典伪装、错误码转换、JDBC驱动行为对齐。任何一环缺失零改造都会露馅。4.2 隐式转换与语句改写内核对SQL的“二次理解”Oracle的类型转换规则非常宽松PostgreSQL系则严格得多。最典型的例子是字符串和数字的比较Oracle里WHERE number_col 123会自动把字符串转成数字在PostgreSQL原版里如果字段是numeric类型和字符串比较会报错或者行为不一致。金仓在Oracle兼容模式下必须把这个隐式转换补上否则一张业务表几千条SQL每一条都带这种写法改造量就大了。另一个经常涉及改写的是空字符串语义。Oracle的就是NULL写入、比较、拼接的行为都和NULL一样。PostgreSQL系里空字符串和NULL是两回事。金仓要实现Oracle兼容就得在解析和执行阶段把空字符串按Oracle语义处理同时提供一个参数来控制这个行为是否开启。我在验证时写了一个小用例SELECT NVL(, empty) FROM dual;Oracle模式下返回empty如果切换成PostgreSQL模式就返回空字符串本身。还有外连接语法。Oracle传统的()外连接写法在PostgreSQL方言里是LEFT JOIN。金仓在内核里要把()语法识别并改写为等价语义改写结果还要保证不出多余行、不丢行。这种“二次理解”能力是内核黑科技的核心体现。4.3 优化器与执行计划语法兼容只是开始一个SQL能解析、能跑出正确结果只是零改造的第一步第二步是性能不能离谱。金仓的优化器基于PostgreSQL系但它做了大量Oracle特性的适配包括Hint语法、统计信息、索引选择策略。Oracle里DBA优化的SQL经常带Hint比如/* INDEX(t idx_name) */、/* LEADING(t1 t2) */。金仓在Oracle兼容模式下要能识别并尊重这些Hint哪怕底层优化器机制不同也要尽量按照Hint意图生成执行计划。我实测过带Hint的SQL和不带Hint的执行计划确实会按Hint路径走这对从Oracle带过来的存量SQL很重要。还有一个常见差异是ROWNUM分页。Oracle的经典分页写法是WHERE ROWNUM 20配合子查询PostgreSQL系通常用LIMIT OFFSET。金仓要在兼容模式下支持ROWNUM并且在优化器层面把它转换成等价的高效计划否则ROWNUM出现在过滤条件里执行计划很容易退化成全表扫描加逐行过滤。4.4 数据字典、系统包与驱动的“伪装”能力应用连接数据库之后不只是执行SQL还会查系统表。很多监控工具、报表平台、ORM框架会往ALL_TABLES、USER_TAB_COLUMNS、V$SESSION这些视图上跑查询。金仓把这些视图做了兼容实现查询出来的字段名、字段类型、行数要跟Oracle对得上否则运维平台一上来就报错。包和存储过程也是重头戏。Oracle业务里常见的DBMS_OUTPUT.PUT_LINE、UTL_FILE、DBMS_LOCK金仓在兼容模式里有对应的包实现。存储过程里如果用了这些系统包迁移后不能报“找不到包”的错误。还有一个容易被忽视的层面是JDBC驱动。金仓有自己的JDBC驱动名字可能是kingbase8但关键是它返回给应用的元数据和Oracle要一致。应用里写ResultSetMetaData.getColumnType()Oracle返回Types.NUMBER金仓也得返回Types.NUMBER否则ORM映射层就会把字段类型判断错轻则类型转换异常重则整张表查询失败。以上这些全部加起来才叫“零改造”缺一块都不行。5. 实操过程记录一次完整的零改造迁移验证5.1 环境准备与迁移评估清单讲完原理记录一下我这次验证的实际操作给想复现的朋友一个模板。源库是Oracle 19c目标库用金仓KingbaseES V8系列具体小版本各环境不一样但不影响验证思路。操作系统都是Linux。第一步不是迁移数据而是先跑兼容性评估把源库的DDL、存储过程、视图、触发器全部导出再用金仓自带的迁移评估工具扫描生成兼容性报告。工具会标出哪些对象可以直接迁移、哪些需要手工处理、哪些完全不支持。我建议评估报告要重点看两类东西一是存储过程里的SQL写法二是应用代码里的动态SQL。静态SQL和DDL迁移工具基本都能处理动态SQL是运行时拼接的工具看不到全貌只能靠应用代码审计来补。我的做法是把应用日志里的SQL全部收集起来去重后人工筛选一遍凡是带Oracle特有写法的地方全部标记出来。环境准备阶段还要确认几个参数字符集、时区、默认日期格式、空字符串语义、大小写敏感设置。这些参数看起来不起眼但会影响全库行为最好在初始化时就按Oracle的规范来配而不是等迁移到一半再改。5.2 JSON测试用例从基本取值到异常路径JSON这块我准备了一张业务表结构类似订单表有一个JSON字段存扩展信息。测试用例分为五组。第一组是基本取值用JSON_VALUE取标量值、用JSON_QUERY取对象和数组、用JSON_TABLE做行转列。重点观察返回类型和NULL语义。第二组是路径表达式嵌套路径、数组下标、通配符、带特殊字符的键名。第三组是错误处理路径不存在时分别用默认的NULL ON ERROR和显式的ERROR ON ERROR对比Oracle和目标库的报错表现。第四组是空值和边界空JSON、空数组、null值、空字符串、嵌套很深的JSON。第五组是性能在JSON列上建表达式索引跑等值过滤和范围查询观察执行计划和耗时。我把这些用例做成一张对比表每个用例记录三列Oracle的结果、金仓的结果、是否一致。不一致的标注严重程度和原因。实测下来90%的用例是一致的不一致的主要集中在带特殊字符的键名和JSON文本的规范化输出上。-- 基本取值用例 SELECT JSON_VALUE(ext_info, $.order.user.name) FROM orders WHERE id 1; -- 行转列用例 SELECT ot.order_id, pt.* FROM orders ot, JSON_TABLE(ot.ext_info, $.items[*] COLUMNS (item_id NUMBER PATH $.id, item_name VARCHAR2(50) PATH $.name) ) pt WHERE ot.id 1;这两条SQL在金仓Oracle兼容模式下都能直接跑结果集和Oracle一致。路径里带中文键名、带Unicode转义的场景我建议单独加用例因为两边对转义符的处理偶有不同。5.3 事务隔离验证并发场景怎么压事务隔离的验证不能只看隔离级别设置要看并发行为。我设计了四组压测场景。第一组是锁等待两个会话同时UPDATE同一行一个提交一个不提交观察第二个会话是等待还是报错等待多久。Oracle默认会一直等待金仓默认有死锁检测和锁超时超时时间是否匹配Oracle的体验要提前确认。第二组是死锁两个会话分别以相反顺序更新两张表观察死锁检测速度和错误消息。第三组是SERIALIZABLE冲突两个会话同时读写相近数据手动构造写偏斜场景观察是否报序列化错误错误码是什么。第四组是可重复读一个事务内两次查询同一张表中间另一个会话提交新数据看两次查询结果是否一致。这个验证最大的价值是暴露问题迁移后应用代码不用改但重试逻辑和锁超时配置可能要调。如果应用原来针对ORA-08177做了重试现在目标库报的错误码变了重试就不生效需要把错误码映射关系搞清楚。测试项Oracle现象金仓现象处理建议并发更新同一行后到者等待等待或超时取决于lock_timeout确认超时时间与业务期望一致双向更新死锁报死锁检测较快报死锁检测时间可调优先改SQL统一锁顺序SERIALIZABLE冲突ORA-08177serialization failure应用重试逻辑要适配新错误码REPEATABLE READOracle不区分事务内快照一致按Oracle语义评估一般无碍5.4 结果评估哪些算真正的零改造验证做完我把差异项分了三类能通过参数解决的、能通过SQL等价改写解决的、必须改应用代码的。第一类最少主要是空字符串语义、默认日期格式、错误码映射这类。第二类多一些集中在JSON路径写法、外连接语法、ROWNUM分页但改写量不大而且不涉及应用层逻辑变化。第三类是真正的红线比如应用深度依赖Oracle的某种私有行为、对锁顺序有特殊要求、或者用了原生驱动里的Oracle私有接口。零改造迁移的边界就在这里它不是“什么都能迁”而是“在兼容评估后绝大多数常规业务能迁”。所以实操上我建议迁移前一定要做一次完整的SQL采集和评估别只靠迁移工具报告。迁移工具擅长处理元数据但对应用动态SQL的覆盖是有限的。6. 常见问题与排查技巧实录6.1 JSON相关报错速查迁移后最常碰到的JSON报错有这么几类。invalid input syntax for type json或者类似错误通常是原库的JSON数据本身不严格合法Oracle的宽松校验放过了目标库的解析器更严格就报错了。排查思路是把具体数据拿出来看找非法字符或多余逗号。JSON_VALUE evaluated to no value这类报错说明路径表达式没匹配到内容并且错误处理是ERROR ON ERROR。Oracle里默认是NULL ON ERROR如果应用显式写了ERROR ON ERROR那报错是正常的如果没写却报错就要检查金仓兼容模式下默认错误处理参数是否和Oracle一致。could not identify an equality operator for type json这多半是直接把json类型字段做了GROUP BY或者DISTINCT。Oracle里JSON可以比较但jsonb的等值比较在某些函数里有限制解决方法是类型转换或者改写SQL不要直接对JSON字段做聚合。6.2 事务隔离和并发问题排查并发问题排查我推荐先看数据库日志里有没有大量死锁和锁超时记录再看应用日志里有没有对应的重试失败。如果迁移后突然出现current transaction is aborted这类错误多半是同一个事务里前面的语句报错了事务被标记为aborted后面的语句都执行不了。这种问题的根源不是事务隔离参数而是前面的SQL在目标库的行为和Oracle不一致。SERIALIZABLE冲突报错和ORA-08177的映射问题前面说过这里补一个排查技巧抓两边的错误码做映射表把Oracle常见的ORA-00001、ORA-00060、ORA-08177、ORA-01555逐个对着目标库的错误码和消息列出来给开发团队一份对照表比让他们在线上猜要高效得多。6.3 “permission should be urwx”这类权限问题怎么处理网上搜索金仓相关问题时经常能看到类似“permission should be urwx”的报错很多是在安装、创建目录对象、或者使用外部表和UTL_FILE访问文件时冒出来的。这个问题的本质是权限模型不一样金仓对目录对象的属主和权限位有明确要求目录权限过宽或者属主不对就会报权限错误。排查步骤就三步一看目录属主是否和数据库进程用户一致二看权限位是不是700或750不能是777三看操作系统层有没有SELinux之类的额外限制。处理命令也简单chown -R kingbase:kingbase /data/kingbase/udir chmod 700 /data/kingbase/udir如果是从Oracle迁移过来的别忘了检查原库的DIRECTORY对象权限配置。原库里DBA可能用GRANT READ, WRITE ON DIRECTORY xxx TO user目标库也要把对应的目录对象建好、授权做好否则应用在调用外部文件读写时就是一堆权限报错看起来像数据库bug其实是迁移漏了对象。6.4 零改造的边界别把“零改造”神话写到最后必须给“零改造”泼一点冷水。零改造是一个强目标它要求迁移工具、内核兼容、驱动适配、运维监控全部到位。任何一个环节有短板都会被业务放大。纯OLTP加常用SQL、中等复杂度的PL/SQL金仓的零改造能力基本能覆盖但如果你有大量Oracle私有特性、复杂分区、高级复制、细粒度锁行为依赖还是得在评估阶段就识别出来该调整的调整该改写的改写。我个人在实际迁移中体会最深的一点是零改造测试绝不能只看正常业务一定要把异常注入进去。模拟锁冲突、模拟超时、模拟脏数据、模拟特殊字符这些场景才真正考验数据库兼容性的成色。JSON和事务隔离这两块是所有迁移验证里最不该省的环节。最后再分享一个技巧验证时保留一份“行为差异清单”把每个不一致都记录成表注明影响范围、触发SQL、备选方案。这份清单不仅迁移时有用上线后排查问题也有用因为它记录了你对两个数据库行为差异的全部认知。迁完不等于结束留下的这份文档才是零改造迁移真正沉淀下来的资产。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进