整个迁移方案,分为两部分:

  1. 全量迁移
  2. 增量迁移

过程描述:

  1. 增量数据收集 (创建oracle表的增量物化视图)
  2. 进行全量复制
  3. 进行增量复制 (并行进行数据校验)
  4. 原库停写,切到新库

回滚方案:

  1. 开启新库到老库的数据回流

部署

下载

1. 源码编译

github地址: https://github.com/alibaba/yugong

git clone https://github.com/alibaba/yugong.git

下载后在yugong目录下,执行

mvn clean install -Dmaven.test.skip -Denv=release

会在dist目录下生成yugong-x.y.z.tar.gz文件

2. 二进制包下载

下载地址: https://github.com/alibaba/yugong/releases

目录结构

解压缩发布包后,可得如下目录结构:

drwxr-xr-x 2 jianghang jianghang  136 2013-09-29 17:19 bin
drwxr-xr-x 3 jianghang jianghang 152 2013-09-29 17:19 conf
drwxr-xr-x 2 jianghang jianghang 1640 2013-09-29 17:19 lib
drwxr-xr-x 2 jianghang jianghang 48 2013-09-29 11:57 logs

修改配置

正常情况下,只需修改下yugong.database的源库和目标库的地址信息,通过yugong.table.white定义本次需要迁移的表,通过yugong.table.mode定义要执行的操作,是全量还是增量等,其他的可以使用默认值.

默认值

参数名字 参数说明 默认值
数据库配置相关    
yugong.database.source.username 
yugong.database.source.password 
yugong.database.source.type 
yugong.database.source.url 
yugong.database.source.encode 
源数据库的相关账户和链接信息 
driver url 示例: 
1.  ORACLE : jdbc:oracle:thin:@10.20.144.29:1521:ointest 
2.  MYSQL : jdbc:mysql://10.20.144.34:3306/test
encode默认为UTF-8,其他无默认值
yugong.database.target.username 
yugong.database.target.password 
yugong.database.target.type 
yugong.database.target.url 
yugong.database.target.encode 
目标数据库的相关账户和链接信息  encode默认为UTF-8,其他无默认值 
yugong.table.white  需要同步表,白名单,定义需要同步的表 
几点说明: 
1. 表名支持like匹配,比如'%'匹配一个或者多个字符,下划线'_'匹配单个字符,可以通过单斜杠\进行转义符定义. 
2. 表明为schema+table name组成,多个表可加逗号分隔 
3. 如果白名单为空,代表整个库所有表,否则按指定的表进行同步

例子:

  1. yugong_example_% (可以匹配yugong_example打头的字符串)
  2. alibaba.yugong_example_test_ (可以匹配alibaba.yugong_example_test1 / alibaba.yugong_example_test2)
yugong.table.black 需要同步表,黑名单,需要忽略同步的表 
配置方式可参考yugong.table.white
无 
yugong.table.mode  运行模式,目前支持的模式为: 
1. MARK (开启增量记录,比如oracle就是创建物化视图) 
2. FULL  (全量模式) 
3. INC  (增量模式) 
4. ALL (自动全量+增量模式) 
5. CHECK (数据对比模式) 
6. CLEAR (清理增量记录,比如oracle就是删除物化视图)
yugong.table.concurrent.enable  多张表之前是否开启并行处理,如果false代表需要串行处理 true
yugong.table.concurrent.size 允许并行处理的表数 5
yugong.table.retry.times  表同步出错后的重试次数 3
yugong.table.retry.interval  表同步出错后的重试时的时间间隔,单位ms 1000
yugong.table.batchApply  是否开启jdbc batch处理 true
yugong.table.onceCrawNum  extractor/applier每个批次最多处理记录数 1000
yugong.table.tpslimit tps限制,0代表不限制 0
yugong.table.ignoreSchema 是否忽略schema同步 
(如果mysql和oracle对应的schema不同,可设置为true)
false
yugong.table.skipApplierException  true代表当applier出现数据库异常时,比如约束键冲突,可对单条出异常的数据进行忽略. 
同时记录skiped record data信息,日志中包含record的所有列信息,包括主键.
false
extractor配置相关    
yugong.extractor.dump  是否记录extractor提取到的所有数据 false
yugong.extractor.concurrent.enable extractor是否开启并行处理,目前主要应用为增量模式反查源表  true
yugong.extractor.concurrent.global extractor是启用全局线程池模式,如果true代表所有extractor任务都使用一组线程池,线程池大小由concurrent.size控制  false
yugong.extractor.concurrent.size 允许并行处理的线程数,需要先开启concurrent.enable该参数才会生效 20
yugong.extractor.noupdate.sleep 增量模式下,出现无变更数据时再次获取数据的sleep时间 1000
yugong.extractor.once 是否强制使用一次性模式,不支持断点续作,可提升效率,约为5倍 false
yugong.extractor.noupdate.thresold 处于增量数据追赶中,超过该值后认为增量任务已完成,会释放资源给下一个table 
如果该值<=0,意味着永远不会退出增量任务,会一直跑.
如果需迁移table数 > table.concurrent.size,则默认为3 
如果需迁移table数 <= table.concurrent.size,则默认为-1
applier配置相关    
yugong.applier.dump  是否记录applier提取到的所有数据 false
yugong.applier.concurrent.enable applier是否开启并行处理  true
yugong.applier.concurrent.global applier是启用全局线程池模式,如果true代表所有applier任务都使用一组线程池,线程池大小由concurrent.size控制  false
yugong.applier.concurrent.size  允许并行处理的线程数,需要先开启concurrent.enable该参数才会生效 20
统计和报警    
yugong.stat.print.interval  统计信息打印频率. 
频率为5,代表,完成5轮extract/applier后,打印一次统计信息
5
yugong.progress.print.interval  打印迁移进度状态,单位分钟 1
yugong.alarm.receiver 报警接收人,支持邮件和手机,逗号分隔  
yugong.alarm.msgcenter.hosts  报警中心地址  
     

启动停止

linux启动 :

sh startup.sh

linux带debug方式启动:(默认使用suspend=n,可设置为y,阻塞等待你remote debug链接成功)

sh startup.sh debug 9099 

linux停止:

sh stop.sh  

几点注意:

  1. linux启动完成后,会在bin目录下生成yugong.pid,stop.sh会读取yugong.pid进行进程关闭
  2. startup.sh默认读取系统环境变量中的which java获得JAVA执行路径,需要设置PATH=$JAVA_HOME/bin环境变量

windows启动:

startup.bat

windows停止:直接关闭终端即可

查看日志

对应日志结构为:

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 ... #代表最近一次无增量数据

ALL(全量+增量)模式日志: (会在${table}/table.log中出现记录)

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

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]

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

统计信息:

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

    {未启动:0,全量中:2,增量中:3,已追上:3,异常数:0}
  • stat统计,会在每个表迁移日志下,输出当前迁移的tps信息
    {总记录数:180000,采样记录数:5000,同步TPS:4681,最长时间:215,最小时间:212,平均时间:213} 

切换流程

  1. 当任务处于追上状态时候,表示已经处于实时同步状态
  2. 后续通过源数据库进行停写,稍等1-2分钟后(保证延时的数据最终得到同步,此时源库和目标库当前数据是完全一致的)
  3. 检查增量持续处于NO_UPDATE状态,可关闭该迁移任务(sh stop.sh),即可发布新程序,使用新的数据库,完成切换的流程.

自定义数据转换

 如果要迁移的oracle和mysql的表结构不同,比如表名,字段名有差异,字段类型不兼容,需要使用自定义数据转换。如果完全相同那就可以跳过此章节

整个数据流为:DB -> Extractor -> DataTranslator -> Applier -> DB,本程序预留DataTranslator接口,允许外部用户自定义数据处理逻辑,比如:

  1. 表名不同
  2. 字段名不同
  3. 字段类型不同
  4. 字段个数不同
  5. 运行过程join其他表的数据做计算等
例子
/**
* 一个迁移的例子,涵盖一些基本转换操作
*
* <pre>
* 例子包含特性:
* 1. schema/table名不同. oracle中为otter2.yugong_example_oracle,mysql中为test.yugong_example_mysql
* 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字段,(简单的用迁移时的当前时间进行填充)
*
* 测试的表结构:
* // oracle表
* create table otter2.yugong_example_oracle
* (
* 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)
* );
*
* // mysql表
* create table test.yugong_example_mysql
* (
* 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)
* );
* </pre>
*
* @author jianghang 2013-10-10 下午3:28:33
*/
public class YugongExampleOracleDataTranslator extends AbstractDataTranslator implements DataTranslator {
public boolean translator(Record record) {
// 1. schema/table名不同
// record.setSchemaName("test");
record.setTableName("yugong_example_mysql"); if (record instanceof IncrementRecord) {
if (IncrementOpType.D == ((IncrementRecord) record).getOpType()) {
// 忽略delete
return super.translator(record);
}
} // 2. 字段名字不同
ColumnValue nameColumn = record.getColumnByName("name");
nameColumn.getColumn().setName("display_name"); // 3. 字段逻辑处理
ColumnValue aliasNameColumn = record.getColumnByName("alias_name");
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");
amountColumn.getColumn().setType(Types.VARCHAR);
amountColumn.setValue(ObjectUtils.toString(amountColumn.getValue())); // 5. 源库多一个字段
record.getColumns().remove(aliasNameColumn); // 6. 目标库多了一个字段
ColumnMeta gmtMoveMeta = new ColumnMeta("gmt_move", Types.TIMESTAMP);
ColumnValue gmtMoveColumn = new ColumnValue(gmtMoveMeta, new Date());
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);
}

}

几点说明:

  1. DataTranslator目前仅支持java扩展,允许用户完成类实现后,将类源文件放置到conf/translator/目录下,yugong启动后会进行动态编译.
  2. DataTranslator目前查找规则会根据表名自动查找,比如需要处理的表为otter2.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表上)

运行模式详细介绍

MARK模式(MARK)

开启增量日志的记录,如果是oracle就是创建物化视图

CLEAR模式(CLEAR)

清理增量日志的记录,如果是oracle就是删除物化视图

全量模式(FULL)

全量模式,顾名思议即为对源表进行一次全量操作,遍历源表所有的数据后,插入目标表.
全量有两种处理方式:

  1. 分页处理:如果源表存在主键,只有一个主键字段,并且主键字段类型为Number类型,默认会选择该分页处理模式. 优点:支持断点续做,对源库压力相对较小。 缺点:迁移速度慢
  2. once处理:通过select * from访问整个源表的某一个mvcc版本的数据,通过cursor.next遍历整个结果集. 优点:迁移速度快,为分页处理的5倍左右。 缺点:源库压力大,如果源库并发修改量大,会导致数据库MVCC版本过多,出现栈错误. 还有就是不支持断点续做.
 特别注意
如果全量模式运行过程中,源库有变化时,不能保证源库最近变化的数据能同步到目标表,这时需要配合增量模式. 具体操作就是:在运行全量模式之前,先开启增量模式的记录日志功能,然后开启全量模式,完成后,再将最近变化的数据通过增量模式同步到目标表

增量模式(INC)

全量模式,顾名思议即为对源表增量变化的数据插入目标表,增量模式依赖记录日志功能.
目前增量模式的记录日志功能,是通过oracle的物化视图功能。

 创建物化视图

CREATE MATERIALIZED VIEW LOG ON ${tableName} with primary key.

  • 运行增量模式之前,需要先开启记录日志的功能,即预先创建物化视图. 特别是配合全量模式时,创建物化视图的时间点要早于运行全量之前,这样才可以保证数据能全部同步到目标表
  • 增量模式没有完成的概念,它只有追上的概念,具体的停止需有业务进行判断,可以看一下切换流程

自动模式(ALL)

自动模式,是对全量+增量模式的一种组合,自动化运行,减少操作成本.
自动模式的内部实现步骤:

  1. 开启记录日志功能. (创建物化视图)
  2. 运行全量同步模式. (全量完成后,自动进入下一步)
  3. 运行增量同步模式. (增量模式,没有完成的概念,所以也就不会自动退出,需要业务判断是否可以退出,可以看一下切换流程)

对比模式(CHECK)

对比模式,即为对源库和目标库的数据进行一次全量对比,验证一下迁移结果. 对比模式为一种可选运行,做完全量/增量/自动模式后,可选择性的运行对比模式,来确保本次迁移的正确性.

yugong诠释的更多相关文章

  1. 重新诠释的OSGi规范

    上周五部门开会讨论新一代产品(基于.net Winform)的设计规范,从设计规范慢慢讨论到体系结构等架构存在的问题,诸如菜单.工具条.状态条.界面布局等不能实现配置化和自动化,子系统之间拥有强依赖, ...

  2. public static void main(String[] args){}函数诠释

    public static void main(String[] args){}函数诠释 主函数的一般写法如下: public static void main(String[] args){-} 下 ...

  3. C++ Reference 的“三位一体”诠释

    C++ 是介于汇编语言与高级语言之间的一种“全能”语言.它的能力是其他任何基于VMA(冯-诺曼架构)计算机的高级程序设计语言无法望其项背的,而性能也只有C语言可与之伯仲. 然而长期以来,喜欢C++和憎 ...

  4. HTML条件注释用法诠释

    HTML条件注释用法诠释 注释内容以样式为例,如下: 1.支持所有IE浏览器 <!--[if IE]> <link rel="stylesheet" href=& ...

  5. Python笔记之 - 一张截图诠释"文件读写" !

    Python笔记之 - 一张截图诠释"文件读写" ! 源代码如下: # 文件读写 str_test1 = "先创建txt文件再写入内容: 我是大帅哥" # wi ...

  6. Spring 配置 Annotation <context:annotation-config> 和 <context:component-scan>标签的诠释及区别

    Spring 开启Annotation <context:annotation-config> 和 <context:component-scan>诠释及区别 <cont ...

  7. 立足“快时尚”,联想笋尖S90怎样诠释“比美更美”?

        现现在."快时尚"已经成为广受年轻人追捧的消费观,从服装界的H&M.ZARA到餐饮界的绿茶.外婆家等等,我们都不难看出,快时尚已成为激发年轻人消费欲望的核心元素,并 ...

  8. 再谈mysql锁机制及原理—锁的诠释

    加锁是实现数据库并发控制的一个非常重要的技术.当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁.加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更 ...

  9. 【ABAP系列】SAP ABAP诠释BDC的OK CODE含义

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP诠释BDC的OK ...

  10. 深度实战玩转算法, Java语言7个经典应用诠释算法精髓

    深度实战玩转算法,以Java语言主讲,通过7款经典好玩游戏,真正将算法用于实际开发,由算法大牛ACM亚洲区奖牌获得者liuyubobobo主讲,看得见的算法,带领你进入一个不一样的算法世界,本套课程共 ...

随机推荐

  1. 力扣1143. 最长公共子序列(dp)

    给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度.如果不存在 公共子序列 ,返回 0 . 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符 ...

  2. OSPF配置常用命令知识总结

    OSPF配置常用命令知识总结 1.display ospf abr-asbr 命令用来显示OSPF的区域边界路由器和自治系统边界路由器信息. [R3]dis ospf abr-asbr OSPF Pr ...

  3. pip更改为国内源

    1. 查看现有默认pip安装源 pip config list 2. 按次修改 添加指定源 pip install numpy -i https://pypi.tuna.tsinghua.edu.cn ...

  4. easyui subGrid实现

    $(function(){ $('#dg').datagrid({ view: detailview, detailFormatter:function(index,row){ return '< ...

  5. Vue源码主体分析

    vue的响应式和react的函数式编程思想是很不同的. vue最基本的原理是对getter和setter的代理模式.以及观察者模式. 当数据发生变化时,通知观察者. 而watcher是通过触发数据的g ...

  6. span服务器控件

    Label Button 这些控件在服务器端都有对应的类可以去实例化一个对象, 可是像 span 这些在服务器没有对应的类可实例的, 在服务器端要实例化一个 span 控件时怎么办呢? 可以使用 Ht ...

  7. 如何在Debian10镜像中设置Nginx引擎模块

    目前,我们较多的服务器WEB环境都是用的Nginx引擎,我们采用服务器的目的是可以获取到更多的资源,而且建站数量是不受限制的.我们可以根据自己需要配置Nginx,可以自定义特定域的设置,允许您在单个服 ...

  8. Access denied You do not have access to chat.openai.com. The site owner may have set restrictions that prevent you from accessing the site.解决办法

    报错 Access denied You do not have access to chat.openai.com. The site owner may have set restrictions ...

  9. Centos系统修改时间临时和永久生效

    Centos系统,必须同时修改系统时间和硬件时间,才可以保证修改有效,单纯的使用date命令修改系统时间,是立即生效,重启后系统还原.具体操作如下: 1.date命令 {查看和修改目前系统本地的时间} ...

  10. google api 后端打点上报

    https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type ...