一、前言
最近在做Oracle 迁移Mysql 项目,涉及到一套数据库多个用户,500+张表的项目,涉及到各种数值转换。根据此次的迁移经验,总结了此次测试,供大家参考。
二、环境准备
1、数据库信息
| IP | 数据类型 | OGG版本 | 数据库 | 用户 |
|---|---|---|---|---|
| 192.168.1.10 | Oracle 11.2.0.3 | ogg_12.3.0.1.4_for_oracle | twodb | two |
| 192.168.1.11 | Mysql 8.35 | ggs_Linux_x64_MySQL_64bit | twodb | root |
2、表信息
- 注:不考虑表涉及的合理性,此示例仅展示不同数据之间的转换

- 其它需求
1、迁移近1周的数据,迁移完成后不丢数据接同步操作;
3、Oracle端
-- Oracle
CREATE TABLE SOURCE_TEST (
ID NUMBER(10) PRIMARY KEY,
PHONE_NO VARCHAR2(20), -- 原始手机号
USER_ID NUMBER(10), -- 数字ID
AMOUNT_STR VARCHAR2(20), -- 字符串金额
CREATE_DATE DATE, -- 创建时间
UPDATE_DATE DATE, -- 更新时间
REMARK VARCHAR2(100)
);
- 存储过程:每秒插入1条数据,每5秒更新1条数据(amt随机± 随机金额)
- 赋权
GRANT EXECUTE ON SYS.DBMS_LOCK TO two;
-- 序列
CREATE SEQUENCE seq_source_test
MINVALUE 1
MAXVALUE 9999999
START WITH 1
INCREMENT BY 1;
- 存储过程
CREATE OR REPLACE PROCEDURE proc_source_test_demo
AS
v_id NUMBER(10); -- 主键ID
v_phone_no VARCHAR2(20); -- 手机号
v_user_id NUMBER(10); -- 用户数字ID
v_amount_str VARCHAR2(20); -- 金额字符串
v_rnd_amount NUMBER; -- 随机变动金额
v_update_id NUMBER(10); -- 要更新的ID
BEGIN
-- 无限循环
WHILE TRUE LOOP
-- =============================================
-- 1. 每秒插入 1 条
-- =============================================
v_id := seq_source_test.NEXTVAL;
-- 生成随机11位手机号
v_phone_no := '1' || FLOOR(DBMS_RANDOM.VALUE(3, 9)) || LPAD(FLOOR(DBMS_RANDOM.VALUE(0, 999999999)), 9, '0');
-- 随机用户ID 1~100000
v_user_id := FLOOR(DBMS_RANDOM.VALUE(1, 100000));
-- 随机金额(字符串类型)
v_amount_str := TO_CHAR(FLOOR(DBMS_RANDOM.VALUE(1, 100000)));
-- 插入数据
INSERT INTO SOURCE_TEST (
ID, PHONE_NO, USER_ID, AMOUNT_STR, CREATE_DATE, UPDATE_DATE, REMARK
) VALUES (
v_id,
v_phone_no,
v_user_id,
v_amount_str,
SYSDATE,
SYSDATE,
'自动生成数据'
);
COMMIT;
DBMS_LOCK.SLEEP(1); -- 等待1秒
-- =============================================
-- 2. 每5秒更新1条(每第5次循环执行)
-- =============================================
IF MOD(seq_source_test.CURRVAL, 5) = 0 THEN
-- 随机取一条已存在的ID
SELECT id INTO v_update_id
FROM (SELECT id FROM SOURCE_TEST ORDER BY DBMS_RANDOM.VALUE)
WHERE ROWNUM = 1;
-- 随机波动 ±10 ~ ±1000
v_rnd_amount := FLOOR(DBMS_RANDOM.VALUE(-1000, 1000));
-- 更新:金额字符串加减后重新赋值 + 更新时间
UPDATE SOURCE_TEST
SET AMOUNT_STR = TO_CHAR(TO_NUMBER(AMOUNT_STR) + v_rnd_amount),
UPDATE_DATE = SYSDATE,
REMARK = '自动更新金额: ' || v_rnd_amount
WHERE ID = v_update_id;
COMMIT;
END IF;
END LOOP;
END;
/
-- 后台运行,脚本:vim proc.sh
#!/bin/sh
source /home/oracle/.bash_profile
export con_user='sqlplus -s two/two'
result=`$con_user << EOF
set serveroutput on ;
BEGIN
proc_source_test_demo;
END;
/
EOF`
echo $result
[oracle@MyOracledb ~]$ sh proc.sh &
4、Mysql端
-- 建目标表
CREATE TABLE TARGET_TEST (
ID int(10) PRIMARY KEY,
PHONE_NO VARCHAR(20),
PHONE_SHORT VARCHAR(10), -- 截取前3位
PHONE_MASK VARCHAR(20), -- 替换后
USER_NO_STR VARCHAR(10), -- 数字转字符串
AMOUNT_NUM decimal(18,2),-- 字符转数字
UPT_TIME datetime, -- 空值判断
OP_TIME datetime, -- OGG当前时间
REMARK VARCHAR(100)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
三、配置并启动同步
- 根据《Oracle迁移Mysql方案》配置OGG环境。
- 迁移配置略,按日期迁移
-- where 条件 TABLE two.SOURCE_TEST,sqlpredicate "where CREATE_DATE>to_date('2026-03-23','yyyy-mm-dd')";
3.1、Oracle 端
3.1.1 添加表级TRANDATA
ggsci
>dblogin userid ggm,password oracle
>add trandata two.SOURCE_TEST
sqlplus / as sysdba
ALTER TABLE two.SOURCE_TEST add SUPPLEMENTAL LOG DATA (ALL) COLUMNS;
3.1.2 配置Extract进程
ggsci
>ADD EXTRACT ext1, TRANLOG, BEGIN now, THREADS 2
-- 用add exttrail 命令创建本地 trail 文件
>add exttrail ./dirdat/e1,extract ext1
>edit params ext1
EXTRACT ext1
SETENV (ORACLE_HOME = "/u01/oracle/11.2.0.4/product")
SETENV (NLS_LANG=AMERICAN_AMERICA.UTF8)
SETENV (NLS_LANGUAGE=AMERICAN_AMERICA.UTF8)
SETENV (NLS_TERRITORY=AMERICAN_AMERICA.UTF8)
SETENV (LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib)
SETENV (NLS_DATE_FORMAT = "YYYY-MM-DD HH24:MI:SS")
SETENV (NLS_TIMESTAMP_FORMAT = "YYYY-MM-DD HH24:MI:SS.FF6")
USERID GGM@twodb, PASSWORD oracle
TRANLOGOPTIONS ASMUSER sys@ASM,ASMPASSWORD oracle
THREADOPTIONS MAXCOMMITPROPAGATIONDELAY 20000
EXTTRAIL ./dirdat/e1
DISCARDFILE ./dirrpt/ext1.dsc,APPEND,MEGABYTES 1024
DYNAMICRESOLUTION
table two.SOURCE_TEST;
3.1.3 配传输进程
# 用 add extract 指定本地 trail 文件
>add extract pump1,exttrailsource ./dirdat/e1,BEGIN now
# 用 add rmttrail 指定远程 trail 文件
>ADD RMTTRAIL ./dirdat/e1, EXTRACT pump1
>edit params pump1
EXTRACT pump25
rmthost 192.168.1.11,mgrport 7809
RMTTRAIL ./dirdat/c7
PASSTHRU
table two.SOURCE_TEST;
3.2、Mysql 端-配置replicat
ggsci
> dblogin sourcedb ogg@192.168.1.11:3306userid ogg,PASSWORD oracle;
> add replicat rep1,exttrail ./dirdat/e1,begin now,checkpointtable ogg.ggs_checkpoint
-- 配置rep1进程
> EDIT PARAMS rep1
REPLICAT rep1
DYNAMICRESOLUTION
HANDLECOLLISIONS
SOURCEDEFS ./dirdef/cross.def
targetdb ogg@172.16.254.5:3006,userid ogg,password oracle
DISCARDFILE ./rep1.dsc, purge, MEGABYTES 100
sqlexec "SET FOREIGN_KEY_CHECKS=0"
MAP two.SOURCE_TEST, TARGET paydb.TARGET_TEST,
COLMAP (
ID = ID,
PHONE_NO = PHONE_NO,
-- 截取前3位
PHONE_SHORT = @STREXT(PHONE_NO, 1, 3),
-- 替换 123 → xx
PHONE_MASK = @STRSUB(PHONE_NO, "123", "xx"),
-- 数字转字符串,左侧补0,总长10
USER_NO_STR = @STRNUM(USER_ID, RIGHTZERO, 10),
-- 字符串转数字
AMOUNT_NUM = @NUMSTR(AMOUNT_STR),
-- 空值判断:CREATE_DATE为空用UPDATE_DATE
UPT_TIME = @IF(@ISNULL(CREATE_DATE), UPDATE_DATE, CREATE_DATE),
-- OGG 当前时间
OP_TIME = @DATENOW(),
REMARK = REMARK
);
3.3 同步及迁移操作的启动顺序
- 1、开启同步的抽取和传输进程
> start ext1
> start pump1
- 2、开启迁移操作,避免数据丢失
> start e_two
- 3、当迁移完成后启动rep1
冲突跳过参数:HANDLECOLLISIONS,当同步完成后,删除此参数
> start rep1
四、ggsci 命令
- 修改进程参数
>alter replicat rep1,exttrail ./dirdat/e1, BEGIN 2026-03-30 16:12
-- 可定义抽取时间进行抽取:
>ALTER EXTRACT ext1, TRANLOG, BEGIN 2026-03-31 19:07
- STATS EXTRACT ext1
输出你会看到:
Total inserts:插入总条数
Total updates:更新总条数
Total deletes:删除总条数
Total operations:总操作数(总行数)
Bytes processed:处理字节量(数据量)
Rate:每秒处理速度
# 打印到文件
ggsci << EOF > /home/oracle/ext1_total.log
STATS ext1, TOTAL
EOF
-
SEND ext1, STATUS
当前读取的日志
处理的 RBA 偏移量(字节数)
延迟
已处理数据大小 -
查看 pump 当前运行状态 + 真实配置
191>info pump1 detail
EXTRACT PUMP1 Last Started 2026-03-31 19:07 Status RUNNING
Checkpoint Lag 00:00:00 (updated 00:00:09 ago)
Process ID 38645
Log Read Checkpoint File /u01/backup/ogg12c/dirdat/a1000000000
2026-03-31 19:11:33.000000 RBA 4764421
Target Extract Trails:
Trail Name Seqno RBA Max MB Trail Type
./dirdat/a1 0 4730233 500 RMTTRAIL
Extract Source Begin End
/u01/backup/ogg12c/dirdat/a1000000000 2026-03-31 15:56 2026-03-31 19:11
./dirdat/a1000000000 * Initialized * 2026-03-31 15:56
/u01/backup/ogg12c/dirdat/a1000000002 * Initialized * First Record
/u01/backup/ogg12c/dirdat/a1000000002 * Initialized * First Record
/u01/backup/ogg12c/dirdat/a1000000002 * Initialized * 2026-03-31 15:55
/u01/backup/ogg12c/dirdat/a1000000002 2026-03-31 15:55 2026-03-31 15:55
./dirdat/a1000000000 * Initialized * 2026-03-31 15:55
Current directory /u01/backup/ogg12c
Report file /u01/backup/ogg12c/dirrpt/PUMP1.rpt
Parameter file /u01/backup/ogg12c/dirprm/pump1.prm
Checkpoint file /u01/backup/ogg12c/dirchk/PUMP1.cpe
Process file /u01/backup/ogg12c/dirpcs/PUMP1.pce
Error log /u01/backup/ogg12c/ggserr.log
五、 针对多表表,规则简单,批量映射
- MAP拼接SQL:对’crt_time’,'upt_time’2个字段值=@DATENOW(),其它字段不变,此SQL仅记录,与测试无关
mysql -P 3306 -u ogg -p'oracle' -e "
SET SESSION group_concat_max_len = 102400;
SELECT
REPLACE(
REPLACE(
CONCAT(
'map twodb.', TABLE_NAME, ' , TARGET twodb.', TABLE_NAME, ',',
'COLMAP (',
GROUP_CONCAT(
CONCAT(COLUMN_NAME, ' = ', IF(COLUMN_NAME IN ('crt_time','upt_time'), '@DATENOW()', COLUMN_NAME))
),
');'
),
'\n', ''), -- 去掉换行
'\r', '') -- 去掉回车
AS result
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'twodb'
and table_name in
('t')
group by TABLE_NAME
ORDER BY ORDINAL_POSITION;" > map.txt
六、问题
OGG-00768
ERROR OGG-00768 DYNSQL: Preparing SQL statement (ID = 0) failed. SQL error (1064). You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%T SET %S WHERE %W' at line 1. ERROR OGG-01668 PROCESS ABENDING.
OGG-00768 + MySQL 1064 语法错误,报错信息:%T SET %S WHERE %W 非法 SQL,进程异常终止,多发生在 UPDATE 主键 场景
- 根本原因
HANDLECOLLISIONS 参数与 MySQL 不兼容
该参数在遇到更新主键时,会把 UPDATE 转成非法的 Oracle 风格 SQL 模板
MySQL 无法识别 %T/%S/%W 占位符,直接报 1064 - 解决:删除参数:HANDLECOLLISIONS
OGG-02171
ERROR OGG-02171 Error reading LCR from data source. Status 509, data source type 0. ERROR OGG-02191 Incompatible record 101 in /data/ogg/dirdat/b2000000000, rba -2 when getting trail header.
trail 文件 b2000000000 格式不兼容、版本不对、已损坏REPLICAT 无法读取,直接崩溃。
- 解决:
-- 将b2000000000 删除,调整pump参数重新传输,重新应用 -- 目标端 stop rep11 rm -rf b2000000000 -- 源端 stop pump11 alter extract pump11,exttrailsource ./dirdat/b2,BEGIN 2026-04-01 14:36 start pump11 -- 目标端 alter replicat rep11,exttrail ./dirdat/b2, BEGIN 2026-04-01 14:36 start rep11
update 转换为insert 导致数据重复
- 方法1:对无主建键表创建主键;
ALTER TABLE two.t_info ADD PRIMARY KEY (id);
- 方法1:KEYCOLS (业务唯一列);
无规划主键的表,可以指示单列或多列做为唯一性处理
MAP two.t_info, TARGET two.t_info, KEYCOLS (业务唯一列); <-- 比如 id、order_no、request_id ;
- 方法2:APPLYNOOPUPDATES:
按全表所有列更新增加参数
| 参数 | 作用 | 备注 |
|---|---|---|
| ALLOWNOOPUPDATES | 无主键表 跳过 UPDATE | 会丢数据 |
| APPLYNOOPUPDATES | 无主键表 强制执行 UPDATE | 会丢数据丢数据,但用所有列更新 |
REPLICAT rep11 APPLYNOOPUPDATES <-- 加这行 SQLEXEC "SET FOREIGN_KEY_CHECKS=0" ...
欢迎赞赏支持或留言指正

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




