Oracle → 金仓 KingbaseES 数据库平滑迁移实操
目标:Oracle → KES V9R2C14(Oracle 兼容模式),Kingbase FlySync 全量+增量平滑迁移 环境:macOS M 系列(Apple Silicon ARM64) + Docker aarch64 镜像 方法论:KCSM 官方五步法(评估 E1 → 改造 E2 → 测试 E3 → 割接 E4 → 回退 E5) 工具链:KDMS 评估(结构迁移 + 4 象限兼容性) + KDTS 全量 + KFS (FlySync) 增量 + KFS 比对服务(数据校验,精简/详细两种模式)+ KReplay 验证 目标读者:金仓 DBA、迁移工程师、国产化项目经理
目录
一、迁移方法论总览(KCSM 五步法 + 决策启发式) 二、测试环境搭建(Oracle + KES 双容器) 三、E1 评估阶段(KDMS 4 象限兼容性扫描) 四、E2 改造阶段(SQL/PLSQL 方言适配) 五、造数据(业务 Schema + 测试数据) 六、E3 测试阶段(KDTS 全量 + KFS 同步) 七、E4 割接阶段(业务不停机切换) 八、E5 回退阶段(30 秒切回源库) 九、性能验证(KWR/KSH/KDDM 三件套) 十、附录(方言对照表 / JDBC / 快速验证命令)
一、迁移方法论总览
1.1 KCSM 五步法(官方迁移操作系统)
「迁移不是导入导出+改 SQL,而是评估→改造→测试→割接→回退的闭环决策系统。」
1.2 决策启发式(关键判断)
启发式 1(停机窗口 vs 兼容率):
停机 ≥ 4h 且 对象兼容率 ≥ 95% → KDTS-WEB 一次性离线迁移,无需 KFS
停机 < 1h 或 RTO 要求 < 5min → 必须 KDTS + KFS + 反向 KFS + 应用单切(本文场景)
启发式 2(PL-SQL 翻译阈值):
翻译率 < 85% → 项目升级 P0 风险,需金仓原厂驻场 ≥ 2 周
启发式 3(字符集与 encoding 绑定):
源 AL32UTF8 → KES initdb 必须同 encoding(不可逆)
启发式 4(5 分钟回滚四件套):
金融/政务核心系统 → 保留源库 ≥ 30 天,反向 KFS 链路保持活跃
启发式 5(无主键表的复制标识):
表无主键或无唯一索引 → KFS 源端 ini 必须配置 replicator.extractor.dbms.autoIdentity=full
否则会出现 KFS 日志持续刷 "alter table xx replicate identity full",同步卡住
启发式 6(KES 严格类型检查):
Oracle 兼容模式下 KES 默认严格类型检查;WHERE int_col = '123' 会报错
如需宽松行为,需显式设置兼容参数(生产环境慎用)
1.3 本文适用场景
本文针对最严格的迁移场景:
✅ 业务停机窗口 < 1 小时(要求几乎不停机) ✅ RTO < 5 分钟(回退时间约束) ✅ Oracle → KES Oracle 兼容模式 ✅ KFS 全量 + 增量同步 ✅ 实验场景采用 macOS ARM64 + Docker 环境
1.4 风险与诚实边界
⚠️ 本文基于 KES V9R2C14 编写,部分信息可能与最终 GA 版有出入。请以金仓官方最新文档为准。
⚠️ KFS 下载、安装、命令路径等以官方文档为准:https://bbs.kingbase.com.cn/documentGuide?recId=c3f448eede450dfbd8cadf68a991b40f
1.5 方案总览与拓扑
本文采用 "KDTS + KFS + 反向 KFS 链路 + 应用切换" 的双轨不停机迁移方案,整体架构如下:
拓扑图:
关键术语约定:
ORA_TO_KES_SYNC) | ||
KES_TO_ORA_REVERSE) | ||
⚠️ 本文适用边界:
KDMS vs KFS 数据校验:KDMS 用于数据库结构迁移和 4 象限兼容性评估;数据一致性校验由 KFS 自带的比对服务提供(精简/详细两种模式),E3 阶段产出物统一使用 KFS 比对服务。 Oracle 端抽取方式:支持三种抽取模式. Logminer 模式(KFS 与 Oracle 分开部署,不破坏 Oracle 容器边界, 但功能受限);若需同步 BLOB/CLOB 大对象或 DDL,可改用 REDO 模式——但 REDO 模式要求 KFS 与 Oracle 同机部署;官方推荐方案是 KFS 的 无侵入方案(Oracle 端仅部署轻量日志代理,解析放在外部"体外机", 且与 REDO 模式功能对齐),详见 §6.1。拓扑图中三条边(实线 Logminer/虚线 REDO/粗线 REDO+日志代理)对应这三种模式。
二、测试环境搭建
2.1 环境总览
为了完成本文提到的迁移,我在 macOS 上用金仓数据库 Oracle 兼容版及 Oracle 官方镜像进行测试验证。
2.2 下载 KES 镜像(Oracle 兼容版,aarch64)
下载地址:
官网下载页:https://www.kingbase.com.cn/download.html Docker 安装文档:https://docs.kingbase.com.cn/cn/KES-V9R2C14/install/02-docker-install Oracle 兼容版产品手册:https://docs.kingbase.com.cn/cn/KES-V9R2C14/introduction/
本文使用的版本:
# 下载地址(用户已下载)
# 文件名:KingbaseES_V009R002C014B0009_aarch64_Docker.tar
# 即 KES V9R2C14,Oracle 兼容版,ARM64 Docker 镜像
导入镜像:
docker load -i ~/Downloads/KingbaseES_V009R002C014B0009_aarch64_Docker.tar
docker images | grep -i kingbase
# 预期输出:
# kingbase_v009r002c014b0009_single_arm:v1 88981b24a950 1.71GB 838MB
2.3 启动 KES 容器
mkdir -p ~/kbdata
docker run -tid --privileged \
-p 5432:54321 \
-v ~/kbdata:/home/kingbase/userdata/ \
-e NEED_START=yes \
-e DB_USER=kingbase \
-e DB_PASSWORD=123456 \
-e DB_MODE=oracle \
--name kingbase \
kingbase_v009r002c014b0009_single_arm:v1 \
/usr/sbin/init
关键参数:
DB_MODE=oracle | ||
-p 5432:54321 | ||
DB_USERDB_PASSWORD | ||
--privileged |
验证 KES 启动:
# 等待 KES 就绪
docker logs -f kingbase 2>&1 | grep -E "database system is ready|startup|ready to accept connections"
# 容器内连接(验证 compatible_mode)
docker exec -it kingbase sh -c "ksql -U SYSTEM -W 123456 -d TESTDB"
-- 连接成功后查看:
SHOW compatible_mode; -- 预期输出:oracle
SHOW server_encoding; -- 预期输出:UTF8
SHOW pg_compat_version; -- 推荐值:12(KES V9R1 内核)
2.4 下载 Oracle Docker 镜像(aarch64)
官方参考:
镜像库:https://github.com/oracle/docker-images/blob/main/OracleDatabase/SingleInstance/README.md ARM64 部署指南:https://github.com/wilfriedago/oracle-database-23ai-free-setup-guide
# 拉取 Oracle 23ai Free(ARM64)
docker pull --platform=linux/arm64 container-registry.oracle.com/database/free:latest-lite
2.5 启动 Oracle 容器
mkdir -p ~/oracle_data
docker run -d --name oracle-lite \
-p 1521:1521 \
-e ORACLE_PWD=123456 \
-e ORACLE_CHARACTERSET=AL32UTF8 \
-v ~/oracle_data:/opt/oracle/oradata \
container-registry.oracle.com/database/free:latest-lite
关键参数:
ORACLE_CHARACTERSET | ||
ORACLE_PWD |
验证 Oracle 启动:
# 等待 Oracle 就绪(约 3-5 分钟)
docker logs -f oracle-lite 2>&1 | grep -E "DATABASE IS READY|starting|Oracle instance started"
# 容器内连接
docker exec -it oracle-lite sqlplus SYSTEM/123456@//localhost/FREEPDB1
# 连接成功会看到:
# Oracle Database 23ai Free Edition
# 宿主机客户端连接
sqlplus SYSTEM/123456@//localhost:1521/FREEPDB1
2.6 验证网络互通
# KES → Oracle
docker exec kingbase sh -c "nc -zv host.docker.internal 1521"
# Oracle → KES
docker exec oracle-lite sh -c "nc -zv host.docker.internal 5432"
三、E1 评估阶段(KDMS 4 象限)
3.1 评估目标
「先把兼容性四象限查清楚,再谈能不能迁。」—— 金仓官方迁移方法论
KDMS 扫描源库,输出 4 个象限的兼容性报告:
任一象限 < 90% → 项目升 P0 风险(模型 1)
3.2 执行 KDMS 扫描
KDMS 工具由金仓工程师提供。本文以 SQL 脚本做"模拟评估",覆盖核心检查项。
Oracle 端对象扫描(示例 SQL):
-- 在 Oracle SYSTEM 用户下执行
CONNECT SYSTEM/123456@//localhost/FREEPDB1;
-- 1. 表和列统计
SELECT OWNER, TABLE_NAME, NUM_ROWS
FROM ALL_TABLES
WHERE OWNER = 'TESTUSER'
ORDERBY NUM_ROWS DESC;
-- 2. 约束检查(主键、外键、CHECK、UNIQUE)
SELECT CONSTRAINT_NAME, TABLE_NAME, CONSTRAINT_TYPE, STATUS
FROM ALL_CONSTRAINTS WHERE OWNER = 'TESTUSER';
-- 3. 索引
SELECT INDEX_NAME, TABLE_NAME, COLUMN_NAME, INDEX_TYPE, UNIQUENESS
FROM ALL_IND_COLUMNS i
JOIN ALL_INDEXES ix ON i.INDEX_NAME = ix.INDEX_NAME AND i.INDEX_OWNER = ix.OWNER
WHERE ix.OWNER = 'TESTUSER';
-- 4. 触发器
SELECT TRIGGER_NAME, TABLE_NAME, TRIGGER_TYPE, STATUS
FROM ALL_TRIGGERS WHERE OWNER = 'TESTUSER';
-- 5. 存储过程/函数/包
SELECT OBJECT_NAME, OBJECT_TYPE, STATUS
FROM ALL_OBJECTS WHERE OWNER = 'TESTUSER'
AND OBJECT_TYPE IN ('PROCEDURE','FUNCTION','PACKAGE','PACKAGE BODY');
-- 6. 序列
SELECT SEQUENCE_NAME, LAST_NUMBER, MAX_VALUE, INCREMENT_BY
FROM ALL_SEQUENCES WHERE SEQUENCE_OWNER = 'TESTUSER';
-- 7. 视图
SELECT VIEW_NAME FROM ALL_VIEWS WHERE OWNER = 'TESTUSER';
-- 8. 字符集检查(关键)
SELECTVALUEFROM NLS_DATABASE_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET';
-- 预期:AL32UTF8(必须与 KES encoding 一致)
KES 端兼容性确认:
docker exec -it kingbase sh -c "ksql -U SYSTEM -W 123456 -d TESTDB"
-- 检查兼容模式(DB_MODE=oracle 已确认)
SHOW compatible_mode; -- 预期:oracle
-- 检查字符集(必须与 Oracle 一致:AL32UTF8 → UTF8)
SHOW server_encoding; -- 预期:UTF8
-- 检查 PG 语法兼容版本
SHOW pg_compat_version; -- 预期:12(KES V9R1 内核对应 PG 12)
-- 检查已安装扩展
SELECT extname, extversion FROM pg_extension ORDERBY extname;
3.3 4 象限评估结论(示例)
(+) | ||
jdbc:oracle:thin: → jdbc:kingbase8: | ||
PL/SQL 翻译率估算(KDMS 报告示例):
存储过程(3个):2 个零修改,1 个 JSON 解析需调整 → 67% 零修改
触发器(3个):3 个零修改 → 100%
包(1个):1 个零修改 → 100%
视图(3个):3 个零修改 → 100%
序列(7个):0 个需改 → 100%
综合翻译率:~92% > 85% 阈值,无需原厂驻场
注:以上为 KDMS 评估报告的示例产出。生产实施前必须用 KDMS 对真实源库扫描,翻译率数值以实际报告为准——示例中的 92% 仅为本文示例 Schema 的估算结果。
四、E2 改造阶段(方言适配)
4.1 高频踩坑点(必须改造)
WHERE a(+)=b | WHERE a LEFT JOIN b | |
SEQ.NEXTVAL | NEXTVAL('SEQ') | |
DBMS_JOB.SUBMIT | ||
RAWTOHEX(x) | SYS.RAWTOHEX(x)HEX(x) | |
SYSDATE | SYSDATE | |
NVLDECODE/ROWNUM/CONNECT BY/DUAL | ||
VARCHAR2NUMBER(p,s)/DATE |
4.2 行为差异清单(不可见坑)
'' | |||
WHERE int_col = '123' | |||
DATE | |||
NUMBER | |||
ROWNUM |
⚠️ 模型 4 提醒:迁移代码必须做行为验证测试。
4.3 改造原则
零修改优先:能用 KES 直接兼容的写法不要改 必改项:DBMS_JOB、 (+)外连接、空串/NULL 行为字符集绑定:源 AL32UTF8 → 目标 UTF8(initdb 时绑定,不可逆) 改造预算:翻译率 < 85% 时上浮 30%(启发式 2)
五、造数据(业务 Schema + 测试数据)
本节在 Oracle 容器中创建示例 Schema,覆盖迁移场景需要的各类对象。
5.1 创建用户和 Schema
-- SYSTEM 用户下执行
CREATETABLESPACE TBS_TEST
DATAFILE'/opt/oracle/oradata/TBS_TEST.dbf'
SIZE100M AUTOEXTENDONNEXT10M MAXSIZEUNLIMITED;
CREATEUSER TESTUSER IDENTIFIEDBY123456
DEFAULTTABLESPACE TBS_TEST
QUOTAUNLIMITEDON TBS_TEST;
GRANTCONNECT, RESOURCE, CREATEVIEW, CREATEPROCEDURE, CREATESEQUENCETO TESTUSER;
5.2 创建表结构
-- 切换到 TESTUSER
CONNECT TESTUSER/123456@//localhost/FREEPDB1;
-- 1. 部门表
CREATETABLE DEPARTMENTS (
DEPT_ID NUMBER(10) PRIMARY KEY,
DEPT_NAME VARCHAR2(100) NOTNULL,
DEPT_LOC VARCHAR2(200),
PARENT_ID NUMBER(10),
STATUSVARCHAR2(10) DEFAULT'ACTIVE',
CREATE_DATE DATEDEFAULTSYSDATE,
CONSTRAINT CHK_STATUS CHECK (STATUSIN ('ACTIVE','INACTIVE','DELETED'))
);
-- 2. 员工表(含自引用外键 MGR_ID)
CREATETABLE EMPLOYEES (
EMP_ID NUMBER(10) PRIMARY KEY,
EMP_NAME VARCHAR2(100) NOTNULL,
EMP_NO VARCHAR2(20) UNIQUENOTNULL,
DEPT_ID NUMBER(10),
HIRE_DATE DATEDEFAULTSYSDATE,
SALARY NUMBER(12,2),
COMM NUMBER(12,2) DEFAULT0,
MGR_ID NUMBER(10),
EMAIL VARCHAR2(100),
STATUSVARCHAR2(10) DEFAULT'ACTIVE',
CONSTRAINT FK_DEPT FOREIGNKEY (DEPT_ID) REFERENCES DEPARTMENTS(DEPT_ID),
CONSTRAINT FK_MGR FOREIGNKEY (MGR_ID) REFERENCES EMPLOYEES(EMP_ID),
CONSTRAINT CHK_SAL CHECK (SALARY >= 0)
);
-- 3. 订单表
CREATETABLE ORDERS (
ORDER_ID NUMBER(20) PRIMARY KEY,
CUST_NAME VARCHAR2(200) NOTNULL,
EMP_ID NUMBER(10),
ORDER_DATE DATEDEFAULTSYSDATE,
STATUSVARCHAR2(20) DEFAULT'PENDING',
TOTAL_AMT NUMBER(18,2),
CREATE_TIME TIMESTAMPDEFAULTCURRENT_TIMESTAMP,
CONSTRAINT FK_ORDER_EMP FOREIGNKEY (EMP_ID) REFERENCES EMPLOYEES(EMP_ID),
CONSTRAINT CHK_ORDER_STATUS CHECK (STATUSIN ('PENDING','CONFIRMED','SHIPPED','COMPLETED','CANCELLED')),
CONSTRAINT CHK_TOTAL CHECK (TOTAL_AMT >= 0)
);
-- 4. 订单明细
CREATETABLE ORDER_ITEMS (
ITEM_ID NUMBER(20) PRIMARY KEY,
ORDER_ID NUMBER(20) NOTNULL,
PROD_ID NUMBER(10),
PROD_NAME VARCHAR2(200),
QTY NUMBER(10) NOTNULL,
UNIT_PRICE NUMBER(18,4),
CONSTRAINT FK_ITEM_ORDER FOREIGNKEY (ORDER_ID) REFERENCES ORDERS(ORDER_ID),
CONSTRAINT CHK_QTY CHECK (QTY > 0)
);
-- 5. 产品表
CREATETABLE PRODUCTS (
PROD_ID NUMBER(10) PRIMARY KEY,
PROD_NAME VARCHAR2(200) NOTNULL,
CATALOG_ID NUMBER(10),
PRICE NUMBER(18,4) NOTNULL,
STOCK_QTY NUMBER(10) DEFAULT0,
LAST_PURCHASE DATE,
STATUSVARCHAR2(10) DEFAULT'ON_SALE',
CONSTRAINT CHK_PROD_STATUS CHECK (STATUSIN ('ON_SALE','DISCONTINUED'))
);
-- 6. 产品分类
CREATETABLE CATALOGS (
CATALOG_ID NUMBER(10) PRIMARY KEY,
CATALOG_NAME VARCHAR2(100) NOTNULL,
PARENT_ID NUMBER(10),
SORT_ORDER NUMBER(5) DEFAULT0
);
-- 7. 操作日志(触发器写入目标表)
CREATETABLE AUDIT_LOG (
LOG_ID NUMBER(20) PRIMARY KEY,
TABLE_NAME VARCHAR2(50),
ACTIONVARCHAR2(20),
OLD_VAL VARCHAR2(4000),
NEW_VAL VARCHAR2(4000),
OPER_USER VARCHAR2(100),
OPER_TIME DATEDEFAULTSYSDATE,
IP_ADDR VARCHAR2(50)
);
5.3 创建序列
CREATESEQUENCE SEQ_DEPT STARTWITH1INCREMENTBY1NOMAXVALUE;
CREATESEQUENCE SEQ_EMP STARTWITH1INCREMENTBY1NOMAXVALUE;
CREATESEQUENCE SEQ_ORDER STARTWITH1INCREMENTBY1NOMAXVALUE;
CREATESEQUENCE SEQ_ITEM STARTWITH1INCREMENTBY1NOMAXVALUE;
CREATESEQUENCE SEQ_PROD STARTWITH1INCREMENTBY1NOMAXVALUE;
CREATESEQUENCE SEQ_CATALOG STARTWITH1INCREMENTBY1NOMAXVALUE;
CREATESEQUENCE SEQ_LOG STARTWITH1INCREMENTBY1NOMAXVALUE;
5.4 创建触发器
-- 触发器1:员工入职自动记录日志
CREATEORREPLACETRIGGER TRG_EMP_INSERT
AFTERINSERTON EMPLOYEES
FOREACHROW
BEGIN
INSERTINTO AUDIT_LOG (LOG_ID, TABLE_NAME, ACTION, NEW_VAL, OPER_USER, OPER_TIME)
VALUES (SEQ_LOG.NEXTVAL, 'EMPLOYEES', 'INSERT',
'{"EMP_ID":' || :NEW.EMP_ID || ',"NAME":"' || :NEW.EMP_NAME || '"}',
USER, SYSDATE);
END;
/
-- 触发器2:部门更新自动记录
CREATEORREPLACETRIGGER TRG_DEPT_UPDATE
AFTERUPDATEON DEPARTMENTS
FOREACHROW
BEGIN
INSERTINTO AUDIT_LOG (LOG_ID, TABLE_NAME, ACTION, OLD_VAL, NEW_VAL, OPER_USER, OPER_TIME)
VALUES (SEQ_LOG.NEXTVAL, 'DEPARTMENTS', 'UPDATE',
'{"DEPT_ID":' || :OLD.DEPT_ID || ',"NAME":"' || :OLD.DEPT_NAME || '"}',
'{"DEPT_ID":' || :NEW.DEPT_ID || ',"NAME":"' || :NEW.DEPT_NAME || '"}',
USER, SYSDATE);
END;
/
-- 触发器3:订单状态变更记录
CREATEORREPLACETRIGGER TRG_ORDER_STATUS
BEFOREUPDATEOFSTATUSON ORDERS
FOREACHROW
WHEN (NEW.STATUS != OLD.STATUS)
BEGIN
INSERTINTO AUDIT_LOG (LOG_ID, TABLE_NAME, ACTION, OLD_VAL, NEW_VAL, OPER_USER, OPER_TIME)
VALUES (SEQ_LOG.NEXTVAL, 'ORDERS', 'STATUS_CHANGE',
'{"ORDER_ID":' || :OLD.ORDER_ID || ',"OLD_STATUS":"' || :OLD.STATUS || '"}',
'{"ORDER_ID":' || :NEW.ORDER_ID || ',"NEW_STATUS":"' || :NEW.STATUS || '"}',
USER, SYSDATE);
END;
/
5.5 创建存储过程
-- 存储过程1:部门统计
CREATEORREPLACEPROCEDURE P_GET_DEPT_STATS(
P_DEPT_ID INNUMBER,
P_EMP_COUNT OUTNUMBER,
P_AVG_SAL OUTNUMBER,
P_TOTAL_SAL OUTNUMBER
) AS
BEGIN
SELECTCOUNT(*), NVL(AVG(SALARY),0), NVL(SUM(SALARY),0)
INTO P_EMP_COUNT, P_AVG_SAL, P_TOTAL_SAL
FROM EMPLOYEES
WHERE DEPT_ID = P_DEPT_ID ANDSTATUS = 'ACTIVE';
END P_GET_DEPT_STATS;
/
-- 存储过程2:创建订单
CREATEORREPLACEPROCEDURE P_CREATE_ORDER(
P_CUST_NAME INVARCHAR2,
P_EMP_ID INNUMBER,
P_ITEMS INVARCHAR2,
P_ORDER_ID OUTNUMBER
) AS
V_ORDER_ID NUMBER;
BEGIN
V_ORDER_ID := SEQ_ORDER.NEXTVAL;
INSERTINTO ORDERS (ORDER_ID, CUST_NAME, EMP_ID, ORDER_DATE, STATUS, TOTAL_AMT)
VALUES (V_ORDER_ID, P_CUST_NAME, P_EMP_ID, SYSDATE, 'PENDING', 0);
P_ORDER_ID := V_ORDER_ID;
END P_CREATE_ORDER;
/
-- 存储过程3:月度工资跑批(含 DBMS_OUTPUT,验证 PL/SQL 包)
-- 注意:KES 不支持 Oracle 的 SQL%ROWCOUNT 隐式游标属性,需要先 INTO 变量
CREATEORREPLACEPROCEDURE P_MONTHLY_SALARY_RUN(P_MONTH INVARCHAR2) AS
V_COUNT NUMBER;
BEGIN
SELECTCOUNT(*) INTO V_COUNT FROM EMPLOYEES WHERESTATUS = 'ACTIVE';
INSERTINTO AUDIT_LOG(LOG_ID, TABLE_NAME, ACTION, NEW_VAL, OPER_USER, OPER_TIME)
SELECT SEQ_LOG.NEXTVAL, 'SALARY_RUN', 'BATCH',
'{"MONTH":"' || P_MONTH || '","EMP_COUNT":' || V_COUNT || '}',
USER, SYSDATE;
DBMS_OUTPUT.PUT_LINE('月度工资跑批完成: ' || P_MONTH || ', 员工数: ' || V_COUNT);
END P_MONTHLY_SALARY_RUN;
/
5.6 创建视图(含一个 ANSI 写法验证)
-- 视图1:员工详情(用 ANSI JOIN,验证 KES 支持)
CREATEORREPLACEVIEW V_EMP_DETAIL AS
SELECT e.EMP_ID, e.EMP_NAME, e.EMP_NO, e.HIRE_DATE,
d.DEPT_NAME, d.DEPT_LOC,
e.SALARY, e.COMM,
m.EMP_NAME AS MGR_NAME, e.EMAIL
FROM EMPLOYEES e
LEFTJOIN DEPARTMENTS d ON e.DEPT_ID = d.DEPT_ID
LEFTJOIN EMPLOYEES m ON e.MGR_ID = m.EMP_ID
WHERE e.STATUS = 'ACTIVE';
-- 视图2:订单汇总
CREATEORREPLACEVIEW V_ORDER_SUMMARY AS
SELECT o.ORDER_ID, o.CUST_NAME, o.ORDER_DATE, o.STATUS, o.TOTAL_AMT,
e.EMP_NAME AS SALES_REP
FROM ORDERS o
LEFTJOIN EMPLOYEES e ON o.EMP_ID = e.EMP_ID
WHERE o.ORDER_DATE >= TRUNC(SYSDATE, 'MM');
-- 视图3:库存预警
CREATEORREPLACEVIEW V_STOCK_ALERT AS
SELECT PROD_ID, PROD_NAME, STOCK_QTY, PRICE,
CASEWHEN STOCK_QTY < 10THEN'LOW'
WHEN STOCK_QTY < 50THEN'MEDIUM'
ELSE'OK'ENDAS STOCK_LEVEL
FROM PRODUCTS
WHERESTATUS = 'ON_SALE'AND STOCK_QTY < 50
ORDERBY STOCK_QTY;
5.7 创建 PL/SQL 包
-- 包规范
CREATEORREPLACEPACKAGE PKG_EMP_MANAGE AS
PROCEDURE HIRE_EMP(P_NAME INVARCHAR2, P_NO INVARCHAR2,
P_DEPT_ID INNUMBER, P_SALARY INNUMBER,
P_EMAIL INVARCHAR2, P_MGR_ID INNUMBERDEFAULTNULL);
PROCEDURE FIRE_EMP(P_EMP_ID IN NUMBER);
FUNCTION GET_EMP_COUNT(P_DEPT_ID IN NUMBER) RETURN NUMBER;
PROCEDURE TRANSFER_EMP(P_EMP_ID IN NUMBER, P_NEW_DEPT_ID IN NUMBER);
END PKG_EMP_MANAGE;
/
-- 包体
CREATEORREPLACEPACKAGEBODY PKG_EMP_MANAGE AS
PROCEDURE HIRE_EMP(P_NAME INVARCHAR2, P_NO INVARCHAR2,
P_DEPT_ID INNUMBER, P_SALARY INNUMBER,
P_EMAIL INVARCHAR2, P_MGR_ID INNUMBERDEFAULTNULL) AS
V_EMP_ID NUMBER;
BEGIN
V_EMP_ID := SEQ_EMP.NEXTVAL;
INSERTINTO EMPLOYEES (EMP_ID, EMP_NAME, EMP_NO, DEPT_ID, HIRE_DATE, SALARY, EMAIL, MGR_ID, STATUS)
VALUES (V_EMP_ID, P_NAME, P_NO, P_DEPT_ID, SYSDATE, P_SALARY, P_EMAIL, P_MGR_ID, 'ACTIVE');
END HIRE_EMP;
PROCEDURE FIRE_EMP(P_EMP_ID IN NUMBER) AS
BEGIN
UPDATE EMPLOYEES SETSTATUS = 'INACTIVE'WHERE EMP_ID = P_EMP_ID;
INSERTINTO AUDIT_LOG (LOG_ID, TABLE_NAME, ACTION, NEW_VAL, OPER_USER, OPER_TIME)
VALUES (SEQ_LOG.NEXTVAL, 'EMPLOYEES', 'FIRE', '{"EMP_ID":' || P_EMP_ID || '}', USER, SYSDATE);
END FIRE_EMP;
FUNCTION GET_EMP_COUNT(P_DEPT_ID IN NUMBER) RETURN NUMBER AS
V_COUNT NUMBER;
BEGIN
SELECTCOUNT(*) INTO V_COUNT FROM EMPLOYEES WHERE DEPT_ID = P_DEPT_ID ANDSTATUS = 'ACTIVE';
RETURN V_COUNT;
END GET_EMP_COUNT;
PROCEDURE TRANSFER_EMP(P_EMP_ID IN NUMBER, P_NEW_DEPT_ID IN NUMBER) AS
BEGIN
UPDATE EMPLOYEES SET DEPT_ID = P_NEW_DEPT_ID WHERE EMP_ID = P_EMP_ID;
INSERTINTO AUDIT_LOG (LOG_ID, TABLE_NAME, ACTION, NEW_VAL, OPER_USER, OPER_TIME)
VALUES (SEQ_LOG.NEXTVAL, 'EMPLOYEES', 'TRANSFER',
'{"EMP_ID":' || P_EMP_ID || ',"NEW_DEPT":' || P_NEW_DEPT_ID || '}',
USER, SYSDATE);
END TRANSFER_EMP;
END PKG_EMP_MANAGE;
/
5.8 插入测试数据
-- 分类(先于产品)
INSERTINTO CATALOGS VALUES (SEQ_CATALOG.NEXTVAL, '电子产品', NULL, 1);
INSERTINTO CATALOGS VALUES (SEQ_CATALOG.NEXTVAL, '办公用品', NULL, 2);
INSERTINTO CATALOGS VALUES (SEQ_CATALOG.NEXTVAL, '电脑配件', 1, 1);
-- 部门
INSERTINTO DEPARTMENTS VALUES (SEQ_DEPT.NEXTVAL, '技术研发部', 'A栋5楼', NULL, 'ACTIVE', SYSDATE);
INSERTINTO DEPARTMENTS VALUES (SEQ_DEPT.NEXTVAL, '市场营销部', 'B栋3楼', NULL, 'ACTIVE', SYSDATE);
INSERTINTO DEPARTMENTS VALUES (SEQ_DEPT.NEXTVAL, '财务部', 'A栋6楼', NULL, 'ACTIVE', SYSDATE);
INSERTINTO DEPARTMENTS VALUES (SEQ_DEPT.NEXTVAL, '人力资源部', 'B栋2楼', NULL, 'ACTIVE', SYSDATE);
-- 员工
INSERTINTO EMPLOYEES VALUES (SEQ_EMP.NEXTVAL, '张总', 'E001', NULL, DATE'2020-01-01', 50000, NULL, NULL, '[email protected]', 'ACTIVE');
INSERTINTO EMPLOYEES VALUES (SEQ_EMP.NEXTVAL, '李经理', 'E002', 1, DATE'2021-03-15', 25000, 5000, 1, '[email protected]', 'ACTIVE');
INSERTINTO EMPLOYEES VALUES (SEQ_EMP.NEXTVAL, '王工', 'E003', 1, DATE'2022-06-01', 15000, NULL, 2, '[email protected]', 'ACTIVE');
INSERTINTO EMPLOYEES VALUES (SEQ_EMP.NEXTVAL, '刘工', 'E004', 1, DATE'2023-01-10', 12000, NULL, 2, '[email protected]', 'ACTIVE');
INSERTINTO EMPLOYEES VALUES (SEQ_EMP.NEXTVAL, '陈经理', 'E005', 2, DATE'2021-07-01', 22000, 8000, 1, '[email protected]', 'ACTIVE');
INSERTINTO EMPLOYEES VALUES (SEQ_EMP.NEXTVAL, '赵销售', 'E006', 2, DATE'2024-02-20', 10000, 20000,5, '[email protected]', 'ACTIVE');
-- 产品
INSERTINTO PRODUCTS VALUES (SEQ_PROD.NEXTVAL, 'ThinkPad X1 Carbon', 3, 12999.00, 50, DATE'2026-01-15', 'ON_SALE');
INSERTINTO PRODUCTS VALUES (SEQ_PROD.NEXTVAL, 'Dell 27寸显示器', 3, 2999.00, 120, DATE'2026-02-01', 'ON_SALE');
INSERTINTO PRODUCTS VALUES (SEQ_PROD.NEXTVAL, '罗技 MX Keys', 2, 599.00, 200, DATE'2026-03-10', 'ON_SALE');
INSERTINTO PRODUCTS VALUES (SEQ_PROD.NEXTVAL, '华为 MateBook 14', 3, 7999.00, 8, DATE'2026-06-20', 'ON_SALE');
INSERTINTO PRODUCTS VALUES (SEQ_PROD.NEXTVAL, 'A4 复印纸(500张)', 2, 25.00, 500, DATE'2026-07-01', 'ON_SALE');
-- 订单
INSERTINTO ORDERS VALUES (SEQ_ORDER.NEXTVAL, '北京科技有限公司', 6, DATE'2026-08-01', 'COMPLETED', 89999.00, SYSTIMESTAMP);
INSERTINTO ORDERS VALUES (SEQ_ORDER.NEXTVAL, '上海贸易公司', 6, DATE'2026-08-05', 'SHIPPED', 45999.00, SYSTIMESTAMP);
INSERTINTO ORDERS VALUES (SEQ_ORDER.NEXTVAL, '深圳创新企业', 6, DATE'2026-08-10', 'PENDING', 15999.00, SYSTIMESTAMP);
INSERTINTO ORDERS VALUES (SEQ_ORDER.NEXTVAL, '广州实业集团', 6, DATE'2026-08-12', 'CONFIRMED', 12999.00, SYSTIMESTAMP);
-- 订单明细
INSERTINTO ORDER_ITEMS VALUES (SEQ_ITEM.NEXTVAL, 1, 1, 'ThinkPad X1 Carbon', 3, 12999.00);
INSERTINTO ORDER_ITEMS VALUES (SEQ_ITEM.NEXTVAL, 1, 2, 'Dell 27寸显示器', 10, 2999.00);
INSERTINTO ORDER_ITEMS VALUES (SEQ_ITEM.NEXTVAL, 2, 1, 'ThinkPad X1 Carbon', 2, 12999.00);
INSERTINTO ORDER_ITEMS VALUES (SEQ_ITEM.NEXTVAL, 2, 4, '华为 MateBook 14', 3, 7999.00);
INSERTINTO ORDER_ITEMS VALUES (SEQ_ITEM.NEXTVAL, 3, 5, '罗技 MX Keys', 10, 599.00);
INSERTINTO ORDER_ITEMS VALUES (SEQ_ITEM.NEXTVAL, 4, 1, 'ThinkPad X1 Carbon', 1, 12999.00);
COMMIT;
5.9 验证数据完整性
SELECT'DEPARTMENTS'AS TBL, COUNT(*) AS CNT FROM DEPARTMENTS
UNIONALLSELECT'EMPLOYEES', COUNT(*) FROM EMPLOYEES
UNIONALLSELECT'PRODUCTS', COUNT(*) FROM PRODUCTS
UNIONALLSELECT'ORDERS', COUNT(*) FROM ORDERS
UNIONALLSELECT'ORDER_ITEMS', COUNT(*) FROM ORDER_ITEMS
UNIONALLSELECT'AUDIT_LOG', COUNT(*) FROM AUDIT_LOG;
-- 预期:
-- DEPARTMENTS: 4
-- EMPLOYEES: 6
-- PRODUCTS: 5
-- ORDERS: 4
-- ORDER_ITEMS: 6
-- AUDIT_LOG: 0(触发器会在 INSERT/UPDATE 时自动写入)
六、E3 测试阶段(KDTS 全量 + KFS 同步)
6.1 Oracle 端前置配置(KFS 前置条件)
⚠️ 重要(基于《KFS 生命周期管理手册》§7.1.1):KFS 对 Oracle 提供两种增量抽取方式,前置条件不同!
本文默认采用 Logminer 模式(KFS 在 Linux 容器,Oracle 在 Docker 容器)。
如果要测试大对象(BLOB/CLOB)或 DDL 同步场景,必须采用 REDO 模式——这意味着 KFS 必须部署在 Oracle 容器内部(同机),生产环境慎用。
💡 金仓 KFS 无侵入增量数据同步方案(推荐生产环境采用该方案),无需KFS与Oracle同机部署, 并且功能齐全:
在 Oracle 端仅部署轻量化的增量日志读取代理服务(导管),只负责实时捕获归档日志,CPU/内存占用极低 抓取的日志通过专线实时传输到 KFS 源端"体外机" 上,由体外机完成解析、缓存、排序等重活 效果:Oracle 端资源占用 ≈ 1% CPU / 500MB 内存,传统集中部署要 10% CPU / 2GB 内存 把 REDO 模式"同机部署"的硬约束变成了"代理 + 远程传输",省下 Oracle 容器资源,又不损失 REDO 优点 适用:核心业务系统、对生产库稳定性要求极高的场景 本文后续默认仍以 Logminer 模式为主线,生产环境则强烈建议优先评估无侵入方案。
无侵入方案专属拓扑:
关键边界:
日志代理服务与 KFS 源端服务不在同一台机器——这是无侵入方案区别于传统 KFS-REDO 集中部署的核心 Oracle 端只增加日志代理(≈ 1% CPU / 500MB),重活在体外机,生产库零侵入 归档日志传输推荐专线,稳定性优于公网;KSync 是金仓的另一种传输手段(取决于实施版本) 对应的 KFS 抽取方式仍是 REDO 模式(不是 Logminer),所以支持大对象、支持 DDL、解析性能高——与原 REDO 模式等价,且无需与 Oracle 实例部署在同一主机
-- Oracle DBA 执行
CONNECT SYS/123456 AS SYSDBA;
-- 1. 开启补充日志
ALTERDATABASEADD SUPPLEMENTAL LOGDATA (ALL) COLUMNS;
ALTERDATABASEADD SUPPLEMENTAL LOGDATA (PRIMARY KEY) COLUMNS;
ALTERDATABASEADD SUPPLEMENTAL LOGDATA (UNIQUE) COLUMNS;
-- 2. 验证
SELECT SUPPLEMENTAL_LOG_DATA_ALL,
SUPPLEMENTAL_LOG_DATA_PK,
SUPPLEMENTAL_LOG_DATA_UI
FROM V$DATABASE;
-- 预期:全部 YES
-- 3. 开启归档(KFS 默认需要归档模式)
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTERDATABASEARCHIVELOG;
ALTERDATABASEOPEN;
-- 5. 验证归档
SELECT log_mode FROM V$DATABASE;
-- 预期:ARCHIVELOG
6.2 安装 KFS(Linux 容器内运行)
💡 KFS 容器是本文方案的"第三个容器"——Oracle 容器(1521)和 KES 容器(54321)之间,需要一个独立的 Linux 容器跑 KFS 同步服务(端口 8090/8091)。三个容器通过
host.docker.internal或宿主机 IP 互通。
KFS 安装包和文档:
产品页:https://www.kingbase.com.cn/product/details_552_584.html 技术社区:https://bbs.kingbase.com.cn/documentGuide?recId=c3f448eede450dfbd8cadf68a991b40f 部署参考:命令行工具手册(包含 fspm / replicator / fsrepctl 等)
部署步骤(Linux 容器内):
# 1. 启动 KFS 容器(macOS ARM64 推荐 arm64v8/ubuntu 或 kingbase 官方基础镜像)
docker run -tid --privileged --name kfs \
-p 8090:8090 \
-p 8091:8091 \
-p 3112:3112 \
-p 11000:11000 \
--add-host=host.docker.internal:host-gateway \
arm64v8/ubuntu:22.04 \
/usr/sbin/init
docker exec -it kfs bash
# 2. 上传 KFS 安装包到 Linux 容器(macOS 宿主机 → Linux KFS 容器)
docker cp ~/Downloads/KingbaseFlySync_xxx_linux.tar.gz kfs:/tmp/
# 3. 解压并配置环境变量
cd /tmp
tar -xzf KingbaseFlySync_xxx_linux.tar.gz
cd KingbaseFlySync
export KFS_HOME=/opt/kingbase/flysync
source${KFS_HOME}/global_env.sh
# 4. 使用 fspm 生成配置 + 安装服务(不是 init-mgr)
${KFS_HOME}/bin/fspm configure # 交互式生成 flysync.ini
${KFS_HOME}/bin/fspm install # 安装同步服务组件
# 5. 启动 replicator 服务
${KFS_HOME}/bin/replicator start
${KFS_HOME}/bin/replicator status # 验证服务状态
# 6. 验证 Manager 健康(管理控制台 8090)
curl -s http://localhost:8090/api/v2/health
# 预期输出:{"status":"UP"}
核心命令清单(基于《KFS 命令行工具参考手册》):
fspm configure/install/delete-service/update | |
replicator start/stop/restart/status | |
fsrepctl status/online/offline/purge/reset/load | |
ddlscan | |
loader | |
repkeyclean |
6.3 KFS 架构与数据流
6.4 配置 KFS 全量+增量同步任务
⚠️ 用
fspm生成 flysync.ini 配置文件,再通过replicator控制服务启停,fsrepctl控制服务状态。
通过 KFS 管理控制台(http://localhost:8090):
创建源端连接(Oracle)
数据源 → 新建 → Oracle
名称: ORA19C_SOURCE
主机: <Oracle 宿主机 IP>
端口: 1521
SID: FREEPDB1
用户: SYSTEM
密码: 123456
字符集: AL32UTF8
补充日志: 已启用(验证)
抽取方式: Logminer 或 REDO(见 6.1 节选择)
创建目标端连接(KES)
数据源 → 新建 → KingbaseES
名称: KES_TARGET
主机: <KES 宿主机 IP>
端口: 54321
数据库: TESTDB
用户: kingbase
密码: 123456
兼容模式: oracle
创建同步服务(在管理控制台或编辑 flysync.ini)
同步管理 → 新建服务
服务名称: ORA_TO_KES_SYNC
源端: ORA19C_SOURCE
目标端: KES_TARGET
同步对象:
- TESTUSER.DEPARTMENTS
- TESTUSER.EMPLOYEES
- TESTUSER.PRODUCTS
- TESTUSER.CATALOGS
- TESTUSER.ORDERS
- TESTUSER.ORDER_ITEMS
- TESTUSER.AUDIT_LOG
同步类型: 全量初始化 + 增量实时同步
启动位置: NOW (增量起点)
DDL 同步: 启用(仅 REDO 模式支持,Logminer 模式不支持 DDL 同步)
或通过配置文件 flysync.ini 关键字段(部分示例):
# flysync.ini 关键配置段(参考《KFS 命令行工具参考手册》)
# [defaults]
# 关键必选项:处理无主键表的复制标识(来自《KES 无中间库不停机迁移方案》:源端 ini 中务必配置)
replicator.extractor.dbms.autoIdentity=full
replicator.service.thlParallelizationStyle=disk
# 启动/停止服务(在 fspm install 之后)
${KFS_HOME}/bin/replicator start
${KFS_HOME}/bin/fsrepctl -service ORA_TO_KES_SYNC status
${KFS_HOME}/bin/fsrepctl -service ORA_TO_KES_SYNC online
⚠️
replicator.extractor.dbms.autoIdentity=full是源端 ini 的必须配置项(来自《KES 无中间库不停机迁移方案》原文):当源端表无主键或无唯一索引时,KFS 会持续在日志里刷alter table xx replicate identity full,同步会卡在这一步。配置autoIdentity=full后 KFS 会自动处理复制标识,无需人工干预。
6.5 启动并验证同步
# 启动同步服务(基于 replicator + fsrepctl)
${KFS_HOME}/bin/replicator start
${KFS_HOME}/bin/replicator status # 验证服务状态
# 查看同步链路状态
${KFS_HOME}/bin/fsrepctl -service ORA_TO_KES_SYNC status
# 查看性能
${KFS_HOME}/bin/fsrepctl perf
# 预期:延迟接近 0(秒级),吞吐量显示每秒同步行数
6.6 KES 端数据一致性校验
数据一致性校验统一使用 KFS 自带的比对服务(精简/详细两种模式,端口 8091)。KDMS 不提供两端数据比对功能,只负责结构迁移和 4 象限兼容性评估。
6.6.1 模式选择
校验频率建议:业务低峰期(如凌晨 3 点)执行;交易类系统按日周期,集采类系统按周周期。差异处理流程详见《KFS 生命周期管理手册》§9.3.1.4。
6.6.2 KFS 比对服务实际调用
通过管控平台(8090)发起比对任务:
# 1. 进入比对服务入口(图形化):管控平台 → 数据比对 → 新建比对任务
# - 服务: ORA_TO_KES_SYNC
# - 模式: 精简 / 详细
# - 对象: TESTUSER.* (按需勾选表)
# 2. 通过 fsrepctl 也可以查询比对状态(命令行方式)
${KFS_HOME}/bin/fsrepctl -service ORA_TO_KES_SYNC status
# 预期:比对进度 100%,差异数 0
6.6.3 辅助手段:人工记录数比对(割接窗口快速验证)
割接窗口(T-5min)场景下时间紧张,可以用人工记录数比对做最朴素的快速验证:
docker exec -it kingbase sh -c "ksql -U SYSTEM -W 123456 -d TESTDB"
-- 逐表对照 COUNT(*)
SELECT'DEPARTMENTS'AS TBL, COUNT(*) AS CNT FROM TESTUSER.DEPARTMENTS
UNIONALLSELECT'EMPLOYEES', COUNT(*) FROM TESTUSER.EMPLOYEES
UNIONALLSELECT'PRODUCTS', COUNT(*) FROM TESTUSER.PRODUCTS
UNIONALLSELECT'ORDERS', COUNT(*) FROM TESTUSER.ORDERS
UNIONALLSELECT'ORDER_ITEMS', COUNT(*) FROM TESTUSER.ORDER_ITEMS
UNIONALLSELECT'AUDIT_LOG', COUNT(*) FROM TESTUSER.AUDIT_LOG;
Oracle 端同样 SQL 比对记录数。生产环境的完整校验仍以 §6.6.2 KFS 比对服务为准。
6.7 增量同步验证(关键)
在 Oracle 端产生增量,观察 KES 端实时同步:
-- Oracle 端:插入新员工(触发器会自动写 AUDIT_LOG)
CONNECT TESTUSER/123456@//localhost/FREEPDB1;
CALL PKG_EMP_MANAGE.HIRE_EMP('钱七', 'E007', 1, 18000, '[email protected]', 2);
CALL PKG_EMP_MANAGE.HIRE_EMP('孙八', 'E008', 2, 12000, '[email protected]', 5);
UPDATE DEPARTMENTS SET DEPT_LOC = 'A栋7楼'WHERE DEPT_ID = 1;
-- 修改订单状态
UPDATE ORDERS SETSTATUS = 'SHIPPED'WHERE ORDER_ID = 3;
COMMIT;
等待 2-3 秒后,KES 端验证:
-- KES 端:检查增量数据
SELECT EMP_ID, EMP_NAME, EMP_NO, SALARY FROM TESTUSER.EMPLOYEES
WHERE EMP_ID IN (7, 8);
-- 验证触发器同步
SELECT * FROM TESTUSER.AUDIT_LOG ORDERBY LOG_ID DESCLIMIT10;
-- 验证 UPDATE 同步
SELECT DEPT_ID, DEPT_LOC FROM TESTUSER.DEPARTMENTS WHERE DEPT_ID = 1;
-- 验证订单状态变更
SELECT ORDER_ID, STATUSFROM TESTUSER.ORDERS WHERE ORDER_ID = 3;
✅ KFS 同步延迟正常应在秒级。如果出现"插入了 10 条但 KES 只同步 5 条"或"延迟持续 > 10 秒",检查 KFS 管理控制台的告警面板。
七、E4 割接阶段(业务不停机)
7.1 割接前 7 步检查清单
SELECT SUPPLEMENTAL_LOG_DATA_ALL FROM V$DATABASE | |||
SELECT log_mode FROM V$DATABASE | |||
sys_stat_statements | |||
fsrepctl -service KES_TO_ORA_REVERSE status |
7.2 业务割接时间线
⚠️ 关键:本文采用 "KDTS + KFS + 反向 KFS 链路 + 应用切换" 的双轨不停机迁移,割接流程中必须包含 KFS 同步方向切换步骤——即 T-13min 把正向 KFS 链路(ORA_TO_KES_SYNC)置为 offline 并 drained,T-8min 切换应用 JDBC。
T-30min ⚠️ 停止 Oracle 端业务写入(应用层或 DB 端)
T-25min ✅ 确认 KFS 同步延迟 = 0
T-15min 📊 启动 KWR 快照(采集基线)
SELECT kwr_snapshot.create_snapshot();
T-13min 🔄 把 ORA_TO_KES_SYNC 链路置为 offline(暂停同步,不再从 Oracle 取增量)
- 命令: fsrepctl -service ORA_TO_KES_SYNC offline
T-10min ⏳ 等待 KFS 链路 drained(关键!避免 KES 端有未应用的增量就切应用)
- 命令: fsrepctl -service ORA_TO_KES_SYNC status
- 预期: pending events = 0,applied seqno 与最新归档一致
- 或执行 KFS 比对服务(精简模式)确认记录数一致
T-8min 🔄 修改应用 JDBC URL:jdbc:oracle:thin: → jdbc:kingbase8:
- 连接串:jdbc:kingbase8://host:54321/TESTDB
- 用户名:TESTUSER(与 Oracle 同名)
- 驱动:com.kingbase8.Driver
T-5min 🧪 应用层冒烟测试(核心 3-5 个 SQL)
T-0min 🚀 业务恢复,指向 KES
T+10min 📊 监控核心业务 SQL 响应时间(P95/P99)
T+30min ✅ 确认无异常,启动新一轮 KWR 快照
T+24h 📊 第二次 KWR 采样,确认稳定
KFS 同步方向切换(与上面时间线并行):
这一步是双轨不停机方案区别于 KDTS-WEB 一次性迁移的关键——切换前先把 KFS 链路置为"末态",避免切应用后 KFS 仍然从 Oracle 写 KES 造成意外抖动。
# T-13min ~ T-10min 之间执行(在应用 JDBC 切换前)
# 1. 把 ORA_TO_KES_SYNC 链路置为 offline(暂停同步,不再从 Oracle 取增量)
${KFS_HOME}/bin/fsrepctl -service ORA_TO_KES_SYNC offline
# 2. 等待 KFS 链路 drained(关键步骤)
# 命令:fsrepctl -service ORA_TO_KES_SYNC status
# 预期:pending events = 0,applied seqno 与最新归档一致
# 或执行 KFS 比对服务(精简模式)确认记录数一致
# 只有 drained 完成后才能继续切应用
# 3. 应用切换完成、业务指向 KES 后,反向 KFS 链路(KES_TO_ORA_REVERSE)保持 offline
# 仅在 E5 触发回退时才置为 online
反向 KFS 链路的预置状态(割接前已经完成):
# 6.x 阶段已经完成:
# - 服务 KES_TO_ORA_REVERSE 已用 fspm 配置好(slave 角色,指向 KES 源端 KUFL)
# - 状态保持 offline,不主动同步
# - 仅在 E5 触发回退时执行:fsrepctl -service KES_TO_ORA_REVERSE online
7.3 应用层切换关键点
// 旧(Oracle)
jdbc:oracle:thin:@//oracle-host:1521/FREEPDB1
// 新(KES Oracle 兼容模式)
jdbc:kingbase8://kes-host:54321/TESTDB
// 驱动类
oracle.jdbc.driver.OracleDriver
→ com.kingbase8.Driver
连接池配置调整:
# 旧(Oracle)
hikari.connection-test-query=SELECT 1 FROM DUAL
hikari.driver-class-name=oracle.jdbc.driver.OracleDriver
# 新(KES)
hikari.connection-test-query=SELECT 1
hikari.driver-class-name=com.kingbase8.Driver
7.4 常见割接问题
ORA-00942: 表不存在 | ||
ERROR: permission denied | GRANT ALL ON SCHEMA testUSER TO testUSER | |
data type incompatibility | ||
八、E5 回退阶段(30 秒切回源库)
8.1 回退条件(任一触发即回退)
❌ KES 端性能 P95 延迟 > Oracle 端 2 倍 ❌ KES 端出现数据不一致(KFS 比对服务校验失败) ❌ 应用层在 KES 端连续报错 ❌ 业务系统核心功能不可用
8.2 30 秒回退步骤
方案 A:应用层切回(最快,30 秒):
# 1. 修改应用配置,将 jdbc:kingbase8:// 改回 jdbc:oracle:thin://
# 2. 重启应用(连接池切换)
# 3. 验证 Oracle 端可读写
方案 B:反向 KFS 链路(应急用):
# KFS 支持双向同步(基于 setrole 命令,参考《KFS 命令行工具参考手册》§6.2.20)
# 1. 把 ORA_TO_KES_SYNC 链路角色切换为 slave,让 KES 端成为新主
${KFS_HOME}/bin/fsrepctl -service ORA_TO_KES_SYNC setrole -role slave \
-url kufl://<KES-host>:<kufl-port>/
# 2. 启动反向 KFS 链路,把 KES 端变更同步回 Oracle
${KFS_HOME}/bin/fsrepctl -service KES_TO_ORA_REVERSE online
# 预期:KES → Oracle 方向同步链路建立,开始把新库变更反向同步回源
8.3 回退后操作
# 1. 停止反向 KFS(避免循环同步)
${KFS_HOME}/bin/replicator stop
${KFS_HOME}/bin/fsrepctl -service KES_TO_ORA_REVERSE offline
# 2. 启动 KFS Oracle → KES 链路(继续验证新库)
${KFS_HOME}/bin/replicator start
${KFS_HOME}/bin/fsrepctl -service ORA_TO_KES_SYNC online
# 3. 排查根因:
# - KWR 快照 → KSH 长会话 → KDDM 索引建议
# - 应用日志报错
# - KFS 告警面板
⚠️ 方案 B 的术语说明:
§1.5 统一术语为"反向 KFS 链路"。"双向同步"在 KFS 内部确实是 setrole切换后的客观状态,但对外叙述避免使用"双向同步"以免被误读为"两边都在实时同步"。割接完成后,正常状态下只有应用 → KES(KES 为单主) 的写入链路,KES → Oracle 的反向 KFS 链路保持 offline,仅在 E5 触发回退时短暂置为 online。 方案 B 的 setrole是双向链路底层机制;具体步骤由 §8.4 源库保留策略兜底。
8.4 源库保留(启发式 4)
金融/政务核心系统:保留源库 ≥ 30 天不销毁,反向 KFS 链路保持活跃,并每月演练 1 次。
普通业务系统建议保留源库 ≥ 7 天,确认业务稳定后再清理。
九、性能验证(KWR/KSH/KDDM 三件套)
「金仓性能调优不是套参数模板,而是 KWR 看全景 → KSH 看会话 → KDDM 给建议 的目标—采样—定位—变更—验证闭环。」—— 模型 6
9.1 KWR 工作负载仓库
-- 立即采样
SELECT kwr_snapshot.create_snapshot();
-- 查最近 5 个快照
SELECT * FROM kwr.snapshot ORDERBY snap_id DESCLIMIT5;
9.2 KSH 会话历史(定位长会话/锁等待)
SELECT pid, query_start, state, query
FROM pg_stat_activity
WHERE state = 'idle in transaction'
ORDERBY query_start LIMIT20;
9.3 sys_stat_statements TOP 10
SELECTquery, calls, total_exec_time, mean_exec_time
FROM sys_stat_statements
ORDERBY total_exec_time DESCLIMIT10;
9.4 KDDM 索引建议
SELECT * FROM kddm.recommendation WHEREtype = 'index';
-- 注意:KDDM 推荐索引必须人工复核,确认符合业务查询模式
9.5 关键参数调优(推荐值)
shared_buffers | ||
work_mem | ||
autovacuum_naptime | ||
idle_in_transaction_session_timeout | ||
statement_timeout |
十、附录
10.1 方言快速对照(Oracle → KES Oracle 模式)
NVL(a,b) | NVL(a,b) | |
DECODE(...) | DECODE(...) | |
ROWNUM | ROWNUM | |
CONNECT BY | CONNECT BY | |
SYSDATE | SYSDATE | |
DUAL | DUAL | |
VARCHAR2 | VARCHAR2 | |
NUMBER(p,s) | NUMERIC(p,s)NUMBER 写法照旧 | CREATE TABLE ... NUMBER(p,s) 语法兼容;底层映射到 NUMERIC(详见《KFS 数据类型映射参考手册》) |
BINARY_DOUBLE | BINARY_DOUBLE | |
BINARY_FLOAT | BINARY_FLOAT | |
DATE | DATE | |
TIMESTAMP | TIMESTAMP | |
INTERVAL | INTERVAL | |
LONG | LONG | |
CLOB | CLOB | |
BLOB | BLOB | |
DBMS_OUTPUTDBMS_LOB/DBMS_STATS | ||
DBMS_JOB | ||
(+) | ||
SEQ.NEXTVAL | NEXTVAL('SEQ') |
10.2 JDBC 连接串速查
// 基础连接
jdbc:kingbase8://host:54321/TESTDB
// 主备集群
jdbc:kingbase8://host1:54321,host2:54321,host3:54321/TESTDB
// 读写分离(V8R6+)
jdbc:kingbase8://host1:54321/TESTDB?READONLYHOSTS=host2,host3&usedispatch=true&dispatchMode=1
// 驱动类
com.kingbase8.Driver
10.3 容器内 SQL 客户端命令速查
# Oracle 容器
docker exec -it oracle-lite sqlplus SYSTEM/123456@//localhost/FREEPDB1
# KES 容器
docker exec -it kingbase sh -c "ksql -U SYSTEM -W 123456 -d TESTDB"
# 验证网络互通
docker exec kingbase sh -c "nc -zv host.docker.internal 1521"
docker exec oracle-lite sh -c "nc -zv host.docker.internal 5432"
# 查看 KES 健康
docker logs -f kingbase 2>&1 | grep "ready to accept connections"
# 查看 Oracle 健康
docker logs -f oracle-lite 2>&1 | grep "DATABASE IS READY"
10.4 参考链接
写在最后
本文的核心思想:
场景先于工具(启发式 1):停机窗口 < 1h → 必须 KDTS + KFS + 反向 KFS + 应用单切 可逆性优先(E5 优先于 E4):反向 KFS 链路必须先于割接就绪 行为差异 > 语法兼容(模型 4):NVL/DECODE 通过 ≠ 行为一致 实测 > 厂商承诺(模型 6):性能数字必须 KWR/KSH/KDDM 三件套验证
边界:本文基于 KES V9R2C14(2026-03 内测版)编写,部分命令路径、参数、行为可能与 GA 版有出入。请以金仓官方最新文档为准。