ITPub博客

首页 > 应用开发 > IT综合 > yugong之单张表oracle到mysql迁移

yugong之单张表oracle到mysql迁移

原创 IT综合 作者:jx_yu 时间:2016-03-31 11:01:43 0 删除 编辑

阿里在3月初开源了yugong,该项目使用纯Java开发,主要作用是进行数据库迁移,目前该项目主要支持从oracle数据库向Mysql和DRDS数据库进行迁移。刚好最近有相关需求,所以尝试了一把yugong,以下是尝试的笔记,供大家参考。


此文:单张表从orale到mysql的迁移,源端oracle和目标端mysql的表结构不完全相同

测试环境

项

源库

目标库

数据库类型

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

源端oracle

#创建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

#创建数据库、用户、授权

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

下载

#目测需要翻墙,下载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);

    }

}

启动yugong

]# 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

#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执行增量变更

#在源库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,代表刚完成处理了增量数据

目标端mysql查看结果

select * from yugong_example_mysql_a;

image

#可以看到全量和后面增量的操作都已经同步

附:yugong对应日志结构为:

logs/

  - yugong/  #系统根日志

     - table.log

  - ${table}/  #每张同步表的日志信息

     - table.log

     - extractor.log

     - applier.log

     - check.log

1.全量完成的日志

(会在yugong/table.log 和 ${table}/table.log中出现记录)

table[OTTER2.TEST_ALL_ONE_PK] is end!

2.增量日志

(会在${table}/table.log中出现记录)

table[OTTER2.TEST_ALL_ONE_PK] now is CATCH_UP ... #代表已经追上,最后一次增量数据小于onceCrawNum数量

table[OTTER2.TEST_ALL_ONE_PK] now is NO_UPDATE ... #代表最近一次无增量数据

3.ALL(全量+增量)模式日志

 (会在${table}/table.log中出现记录)

table [OTTER2.TEST_ALL_ONE_PK] full extractor is end , next auto start inc extractor #出现这条代表全量已经完成,进入增量模式

4.CHECK日志

(会在${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]

5.同步过程数据日志

会通过extractor.log/applier.log分别记录extractor和applier的数据记录,因为有DataTranslator的存在,两者记录可能不一致,所以分开两份记录.

统计信息

progress统计,会在主日志下,输出当前全量/增量/异常表的数据,可通过该日志,全局把握整个迁移任务的进度,输出类似:

{未启动:0,全量中:2,增量中:3,已追上:3,异常数:0}

stat统计,会在每个表迁移日志下,输出当前迁移的tps信息

{总记录数:180000,采样记录数:5000,同步TPS:4681,最长时间:215,最小时间:212,平均时间:213}

异常处理

表同步的log error

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开发人员 好像已经修复此问题

 

参考链接:

https://github.com/alibaba/yugong/wiki/QuickStart

https://github.com/alibaba/yugong/wiki/AdminGuide

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/27000195/viewspace-2072904/ ,如需转载,请注明出处,否则将追究法律责任。

上一篇: Innodb 锁定读
请登录后发表评论 登录
全部评论

注册时间:2012-05-23

  • 博文量
    80
  • 访问量
    877528