首页 > 应用开发 > IT综合 > yugong之单张表oracle到mysql迁移
阿里在3月初开源了yugong,该项目使用纯Java开发,主要作用是进行数据库迁移,目前该项目主要支持从oracle数据库向Mysql和DRDS数据库进行迁移。刚好最近有相关需求,所以尝试了一把yugong,以下是尝试的笔记,供大家参考。
|
项 |
源库 |
目标库 |
|
数据库类型 |
ORACLE |
MYSQL |
|
用户 |
test |
test |
|
密码 |
test |
test |
|
URL |
jdbc:oracle:thin:@127.0.0.1:1521:test |
jdbc:mysql://127.0.0.1:3306/test |
|
表名称 |
yugong_example_a |
yugong_example_mysql_a |
#创建test用户,并且授权(此处由于测试环境直接给个DBA角色)
SQL> create user test identified by test;
SQL> grant dba to test;
注:oracle全量基于JDBC拉取数据,增量基于物化视图来实现,所以这里需要给oracle数据库账号开启特殊权限:
GRANT SELECT,INSERT,UPDATE,DELETE ON XXX TO XXX; #常见CRUD权限
GRANT CREATE ANY MATERIALIZED VIEW TO XXX;
GRANT DROP ANY MATERIALIZED VIEW TO XXX;
#创建测试表
create table yugong_example_a(
id NUMBER(11) ,
name varchar2(32) ,
alias_name char(32) default ' ' not null,
amount number(11,2),
score number(20),
text_b blob,
text_c clob,
gmt_create date not null,
gmt_modified date not null,
CONSTRAINT yugong_example_oracle_pk_id PRIMARY KEY (id)
);
#插入2条测试数据
insert into yugong_example_oracle values(1,'ljh','agapple',10.2,100, NULL , NULL ,sysdate,sysdate);
insert into yugong_example_oracle values(2,'yugong','yugong',16.88,2088, NULL , NULL ,sysdate,sysdate);
commit;
#创建数据库、用户、授权
mysql> create database test;
mysql> grant all on test.* to 'test'@'%' identified by 'test';
mysql> flush privileges;
#创建测试表
create table yugong_example_mysql_a
( id bigint(20) unsigned auto_increment,
display_name varchar(128) ,
amount varchar(32),
score bigint(20) unsigned ,
text_b blob,
text_c text,
gmt_create timestamp not null,
gmt_modified timestamp not null,
gmt_move timestamp not null,
CONSTRAINT yugong_example_mysql_pk_id PRIMARY KEY (id)
);
#目测需要翻墙,下载yugong
Wget https://github.com/alibaba/yugong/releases/download/yugong-1.0.0/yugong-1.0.0.tar.gz
#解压到安装目录
]# mkdir /data/yugong
]# tar -zxvf yugong-1.0.0.tar.gz -C /data/yugong
]# cd /data/yugong
]# vi conf/yugong.properties
#源库配置
yugong.database.source.username=test
yugong.database.source.password=test
yugong.database.source.type=ORACLE
yugong.database.source.url=jdbc:oracle:thin:@127.0.0.1:1521:test
yugong.database.source.encode=UTF-8
yugong.database.source.poolSize=30
#目标库配置
yugong.database.target.url=jdbc:mysql://127.0.0.1:3306/test
yugong.database.target.username=test
yugong.database.target.password=test
yugong.database.target.type=MYSQL
yugong.database.target.encode=UTF-8
yugong.database.target.poolSize=30
yugong.table.batchApply=true
yugong.table.onceCrawNum=1000
yugong.table.tpsLimit=0
# use connection default schema
yugong.table.ignoreSchema=false
# skip Applier Load Db failed data
yugong.table.skipApplierException=false
#需要同步表的黑白名单:表可加逗号分隔,白名单为空,代表整个库所有表
#yugong.table.white=yugong_example_join,yugong_example_oracle,yugong_example_two
yugong.table.white= yugong_example_a
yugong.table.black=
# tables use multi-thread enable or disable
yugong.table.concurrent.enable=true
# tables use multi-thread size
yugong.table.concurrent.size=5
# retry times
yugong.table.retry.times = 3
# retry interval or sleep time (ms)
yugong.table.retry.interval = 1000
# MARK/FULL/INC/ALL(REC+FULL+INC)/CHECK/CLEAR
yugong.table.mode=ALL # (自动全量+增量模式)
# yugong extractor
yugong.extractor.dump=false
yugong.extractor.concurrent.enable=true
yugong.extractor.concurrent.size=20
yugong.extractor.noupdate.sleep=1000
yugong.extractor.noupdate.thresold=0
yugong.extractor.once=false
# {0} is all columns , {1}.{2} is schemaName.tableName , {3} is primaryKey
#yugong.extractor.sql=select /*+parallel(t)*/ {0} from {1}.{2} t
#yugong.extractor.sql=select * from (select {0} from {1}.{2} t where {3} > ? order by {3} asc) where rownum <= ?
# yugong applier
yugong.applier.concurrent.enable=true
yugong.applier.concurrent.size=20
yugong.applier.dump=false
# stats
yugong.stat.print.interval=5
yugong.progress.print.interval=1
# alarm email
yugong.alarm.email.host = smtp.163.com
yugong.alarm.email.username = test@163.com
yugong.alarm.email.password =
yugong.alarm.email.stmp.port = 465
yugong.alarm.receiver=test@163.com
注:上面红色部分按照实际环境修改,这里是测试而已,其他选项暂时都是默认的,具体的选项说明参考:
https://github.com/alibaba/yugong/wiki/AdminGuide
使用过程中,可以根据实际需要配置
如果要迁移的oracle和mysql的表结构不同,比如表名,字段名有差异,字段类型不兼容,需要使用自定义数据转换。如果完全相同那就可以跳过此章节
整个数据流为:DB -> Extractor -> DataTranslator -> Applier -> DB,本程序预留DataTranslator接口,允许外部用户自定义数据处理逻辑,比如:
1. 表名不同
2. 字段名不同
3. 字段类型不同
4. 字段个数不同
5. 运行过程join其他表的数据做计算等
1.DataTranslator目前仅支持java扩展,允许用户完成类实现后,将类源文件放置到conf/translator/目录下,yugong启动后会进行动态编译.
2.DataTranslator目前查找规则会根据表名自动查找,比如需要处理的表为dbname.test_all_one_pk,查找的时候会将test_all_one_pk转化为TestAllOnePk + 固定DataTranslator后缀. (如果当前classpath中存在,优先使用classpath,如果不存在,则到conf/translator中查找该名字的java文件进行动态编译)
3.目前提供了几个样例,可参见解压后的conf/translator/目录
a. YugongExampleOracleDataTranslator (当前例子,介绍oracle一张表和mysql一张表之间的转换处理)
b. YugongExampleJoinDataTranslator (介绍oracle多张表和mysql一张表之间的转换处理,oracle中会通过一张表为主表,运行时join查询出其他表数据,合并同步到mysql)
c. YugongExampleTwoDataTranslator (介绍oracle一张表和mysql多张表之间的转换处理,oracle的一张大表数据,可运行时拆分后输出到多张mysql表上)
综上,如果源端和目标端的表存在表名称、字段名、数据类型、字段个数等不同,则迁移需要配置表名称对应的DataTranslator,也就是有多少张存在不同的表相应就需要配置多少个DataTranslator。如果懂java的同学可以通过类的继承(实现同一类型的不同选项)的配置。
上文准备的测试环境的源端oracle的表yugong_example_a和目标端mysql的表yugong_example_mysql_a有如下不同的地方:
1. table名不同. oracle中为yugong_example_a,mysql中为yugong_example_mysql_a
2. 字段名字不同. oracle中的name字段,映射到mysql的display_name
3. 字段逻辑处理. mysql的display_name字段数据来源为oracle库的:name+'('alias_name+')'
4. 字段类型不同. oracle中的amount为number类型,映射到mysql的amount为varchar文本型
5. 源库多一个字段. oracle中多了一个alias_name字段
6. 目标库多了一个字段.mysql中多了一个gmt_move字段,(简单的用迁移时的当前时间进行填充)
故,需要根据不同的地方定义DataTranslator.java,下文的YugongExampleADataTranslator.java中配置这里的6项不同之处。
根据DataTranslator目前查找规则会根据表名自动查找
#源库oracle的表为yugong_example_mysql_a,故对应conf/translator/YugongExampleADataTranslator.java
]# vi conf/translator/YugongExampleADataTranslator.java
//由文件名可定位源表为:yugong_example_mysql_a
package com.taobao.yugong.translator;
import java.sql.Types;
import java.util.Date;
import org.apache.commons.lang.ObjectUtils;
import com.taobao.yugong.common.db.meta.ColumnMeta;
import com.taobao.yugong.common.db.meta.ColumnValue;
import com.taobao.yugong.common.model.record.Record;
public class YugongExampleADataTranslator extends AbstractDataTranslator implements DataTranslator {
public boolean translator(Record record) {
// 1. schema/table名不同
// record.setSchemaName("test");
record.setTableName("yugong_example_mysql_a");
//至此可知道源表为yugong_example_a,目标表为yugong_example_mysql
// 2. 字段名字不同,
//源表的name字段,目标表中叫display_name
ColumnValue nameColumn = record.getColumnByName("name");
if (nameColumn != null) {
nameColumn.getColumn().setName("display_name");
}
// 3. 字段逻辑处理
ColumnValue aliasNameColumn = record.getColumnByName("alias_name");
if (aliasNameColumn != null) {
StringBuilder displayNameValue = new StringBuilder(64);
displayNameValue.append(ObjectUtils.toString(nameColumn.getValue()))
.append('(')
.append(ObjectUtils.toString(aliasNameColumn.getValue()))
.append(')');
nameColumn.setValue(displayNameValue.toString());
}
// 4. 字段类型不同
ColumnValue amountColumn = record.getColumnByName("amount");
if (amountColumn != null) {
amountColumn.getColumn().setType(Types.VARCHAR);
amountColumn.setValue(ObjectUtils.toString(amountColumn.getValue()));
}
// 5. 源库多一个字段
record.removeColumnByName("alias_name");
// 6. 目标库多了一个字段
ColumnMeta gmtMoveMeta = new ColumnMeta("gmt_move", Types.TIMESTAMP);
ColumnValue gmtMoveColumn = new ColumnValue(gmtMoveMeta, new Date());
gmtMoveColumn.setCheck(false);// 该字段不做对比
record.addColumn(gmtMoveColumn);
// ColumnValue text_c = record.getColumnByName("text_c");
// try {
// text_c.setValue(new String((byte[]) text_c.getValue(), "GBK"));
// } catch (UnsupportedEncodingException e) {
// e.printStackTrace();
// }
return super.translator(record);
}
}
]# cd /data/yugong
]# bin/startup.sh
bin/startup.sh: line 54: get_pid: command not found
cd to /data/yugong/bin for workaround relative path
LOG CONFIGURATION : /data/yugong/bin/../conf/logback.xml
yugong conf : /data/yugong/bin/../conf/yugong.properties
CLASSPATH :/data/yugong/bin/../conf:/data/yugong/bin/../lib/yugong-1.0.0.jar:/data/yugong/bin/../lib/spring-2.5.6.jar:/data/yugong/bin/../lib/slf4j-api-1.6.0.jar:/data/yugong/bin/../lib/oro-2.0.8.jar:/data/yugong/bin/../lib/ojdbc14-10.2.0.3.0.jar:/data/yugong/bin/../lib/mysql-connector-java-5.1.35.jar:/data/yugong/bin/../lib/mail-1.4.7.jar:/data/yugong/bin/../lib/logback-core-1.0.6.jar:/data/yugong/bin/../lib/logback-classic-1.0.6.jar:/data/yugong/bin/../lib/jcl-over-slf4j-1.6.0.jar:/data/yugong/bin/../lib/guava-r09.jar:/data/yugong/bin/../lib/fastjson-1.2.8.jar:/data/yugong/bin/../lib/druid-1.0.17.jar:/data/yugong/bin/../lib/commons-logging-1.1.1.jar:/data/yugong/bin/../lib/commons-lang-2.6.jar:/data/yugong/bin/../lib/commons-io-2.4.jar:/data/yugong/bin/../lib/commons-configuration-1.9.jar:/data/yugong/bin/../lib/activation-1.1.jar:.:/usr/java/jdk1.6.0_31/lib:/usr/java/jdk1.6.0_31/jre/lib:
cd to /data/yugong for continue
#log的位置,
]# ls /data/yugong/logs/ -l
drwxr-xr-x 2 root root 4096 3月 28 11:11 TEST.YUGONG_EXAMPLE_A
drwxr-xr-x 2 root root 4096 3月 28 11:11 yugong
注:你会发现logs目录下有同步的源库.表命名的文件夹
其中:yugong目录下的table.log是主日志
源库.表命名的目录下的table.log是此表同步的日志
#查看yugong的主log
]# tail -f /data/yugong/logs/yugong/table.log
2016-03-28 11:11:21.516 [main] INFO com.taobao.yugong.YuGongLauncher - ## start the YuGong.
2016-03-28 11:11:21.598 [main] INFO com.taobao.yugong.controller.YuGongController - check source database connection ...
2016-03-28 11:11:21.625 [main] INFO com.taobao.yugong.controller.YuGongController - check source database is ok
2016-03-28 11:11:21.625 [main] INFO com.taobao.yugong.controller.YuGongController - check target database connection ...
2016-03-28 11:11:21.645 [main] INFO com.taobao.yugong.controller.YuGongController - check target database is ok
2016-03-28 11:11:21.646 [main] INFO com.taobao.yugong.controller.YuGongController - check source tables read privileges ...
2016-03-28 11:11:21.771 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
2016-03-28 11:11:22.933 [main] INFO com.taobao.yugong.controller.YuGongController - check source tables is ok.
2016-03-28 11:11:23.572 [main] INFO com.taobao.yugong.controller.YuGongController - ## prepare start tables[1] with concurrent[5]
2016-03-28 11:11:23.975 [main] INFO com.taobao.yugong.YuGongLauncher - ## the YuGong is running now ......
2016-03-28 11:11:23.976 [YuGongInstance-TEST.YUGONG_EXAMPLE_A] INFO com.taobao.yugong.controller.YuGongInstance - table[TEST.YUGONG_EXAMPLE_A] is start
2016-03-28 11:11:23.980 [main] INFO com.taobao.yugong.YuGongLauncher -
[YuGong Version Info]
[version ]
[hexVeision]
[date ]2016-03-05 02:02:14
[branch ]master
[url ]git@github.com:alibaba/yugong.git
2016-03-28 11:12:23.578 [pool-2-thread-2] INFO com.taobao.yugong.common.stats.ProgressTracer - {未启动:0,全量中:0,增量中:0,已追上:1,异常数:0}
2016-03-28 11:12:23.579 [pool-2-thread-2] INFO com.taobao.yugong.common.stats.ProgressTracer - 已完成:[TEST.YUGONG_EXAMPLE_A]
2016-03-28 11:13:23.579 [pool-2-thread-2] INFO com.taobao.yugong.common.stats.ProgressTracer - {未启动:0,全量中:0,增量中:0,已追上:1,异常数:0}
2016-03-28 11:13:23.579 [pool-2-thread-2] INFO com.taobao.yugong.common.stats.ProgressTracer - 已完成:[TEST.YUGONG_EXAMPLE_A]
#查看表同步的log
]# tail -f /data/yugong /logs/TEST.YUGONG_EXAMPLE_A/table.log
2016-03-28 11:11:23.730 [main] INFO c.t.yugong.extractor.oracle.OracleRecRecordExtractor - create mlog successed. sql : CREATE MATERIALIZED VIEW LOG ON TEST.YUGONG_EXAMPLE_A with primary key
2016-03-28 11:11:23.749 [main] INFO c.t.y.e.o.OracleFullRecordExtractor$ContinueExtractor - TEST.YUGONG_EXAMPLE_A start postion:0
2016-03-28 11:11:23.770 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-2} inited
2016-03-28 11:11:23.975 [main] INFO com.taobao.yugong.controller.YuGongInstance - table[TEST.YUGONG_EXAMPLE_A] start successful. extractor:com.taobao.yugong.extractor.oracle.OracleAllRecordEx
tractor , applier:com.taobao.yugong.applier.AllRecordApplier
2016-03-28 11:11:24.039 [YuGongInstance-TEST.YUGONG_EXAMPLE_A] INFO c.t.yugong.extractor.oracle.OracleAllRecordExtractor - table [TEST.YUGONG_EXAMPLE_A] full extractor is end , next auto star
t inc extractor
2016-03-28 11:11:24.786 [YuGongInstance-TEST.YUGONG_EXAMPLE_A] INFO c.t.y.e.oracle.OracleMaterializedIncRecordExtractor - table[TEST.YUGONG_EXAMPLE_A] now is NO_UPDATE ...
2016-03-28 11:11:25.790 [YuGongInstance-TEST.YUGONG_EXAMPLE_A] INFO c.t.y.e.oracle.OracleMaterializedIncRecordExtractor - table[TEST.YUGONG_EXAMPLE_A] now is NO_UPDATE ...
日志中:
1.full extractor is end , next auto start inc extractor #代表全量迁移已完成,自动进入增量模式
2.now is NO_UPDATE #代表增量表暂时无日志
#在源库oracle上对源表进行增量变更
insert into yugong_example_a values(3,'test','test',88,188, NULL , NULL ,sysdate,sysdate) ;
update yugong_example_a set alias_name = 'superman' where id = 1;
#查看表同步日志
]# tail -f /data/yugong /logs/TEST.YUGONG_EXAMPLE_A/table.log
2016-03-28 13:08:35.132 [YuGongInstance-TEST.YUGONG_EXAMPLE_A] INFO c.t.y.e.oracle.OracleMaterializedIncRecordExtractor - table[TEST.YUGONG_EXAMPLE_A] now is NO_UPDATE ...
2016-03-28 13:08:36.136 [YuGongInstance-TEST.YUGONG_EXAMPLE_A] INFO c.t.y.e.oracle.OracleMaterializedIncRecordExtractor - table[TEST.YUGONG_EXAMPLE_A] now is CATCH_UP ...
2016-03-28 13:08:36.237 [YuGongInstance-TEST.YUGONG_EXAMPLE_A] INFO c.t.y.e.oracle.OracleMaterializedIncRecordExtractor - table[TEST.YUGONG_EXAMPLE_A] now is NO_UPDATE ..
# 会瞬间出现now is CATCH_UP,代表刚完成处理了增量数据
select * from yugong_example_mysql_a;
#可以看到全量和后面增量的操作都已经同步
logs/
- yugong/ #系统根日志
- table.log
- ${table}/ #每张同步表的日志信息
- table.log
- extractor.log
- applier.log
- check.log
(会在yugong/table.log 和 ${table}/table.log中出现记录)
table[OTTER2.TEST_ALL_ONE_PK] is end!
(会在${table}/table.log中出现记录)
table[OTTER2.TEST_ALL_ONE_PK] now is CATCH_UP ... #代表已经追上,最后一次增量数据小于onceCrawNum数量
table[OTTER2.TEST_ALL_ONE_PK] now is NO_UPDATE ... #代表最近一次无增量数据
(会在${table}/table.log中出现记录)
table [OTTER2.TEST_ALL_ONE_PK] full extractor is end , next auto start inc extractor #出现这条代表全量已经完成,进入增量模式
(会在${table}/check.log中出现diff记录)
-----------------
- Schema: yugong , Table: test_all_one_pk
-----------------
---Pks
ColumnValue[column=ColumnMeta[index=0,name=ID,type=3],value=2576]
---diff
ColumnMeta[index=3,name=AMOUNT,type=3] , values : [0] vs [0.0]
会通过extractor.log/applier.log分别记录extractor和applier的数据记录,因为有DataTranslator的存在,两者记录可能不一致,所以分开两份记录.
progress统计,会在主日志下,输出当前全量/增量/异常表的数据,可通过该日志,全局把握整个迁移任务的进度,输出类似:
{未启动:0,全量中:2,增量中:3,已追上:3,异常数:0}
stat统计,会在每个表迁移日志下,输出当前迁移的tps信息
{总记录数:180000,采样记录数:5000,同步TPS:4681,最长时间:215,最小时间:212,平均时间:213}
ERROR com.taobao.yugong.controller.YuGongInstance - retry 1 ,something error happened. caused by com.taobao.yugong.exception.YuGongException: com.taobao.yugong.exception.YuGongException: table[TEST.null] is not found
最终发现,在最初版本的yugong对表使用yugong进行过同步,则会在conf/positioner目录下产生一个SHCENMA_TABLE.dat文本,如对表test. yugong_example_a进行过同步,对应的会有:
/data/yugong/conf/positioner/TEST_YUGONG_EXAMPLE_A.dat 文件。
如果下次(次文件存在的情况下)再次对相同的表进行yugong同步,则会报错,表同步的log中会出现:
table[TEST.null] is not found的异常
解决办法:删除此文件(/data/yugong/conf/positioner/TEST_YUGONG_EXAMPLE_A.dat),再启动yugong。
新版本中yugong开发人员 好像已经修复此问题
参考链接:
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/27000195/viewspace-2072904/ ,如需转载,请注明出处,否则将追究法律责任。