yugong诠释
整个迁移方案,分为两部分:
- 全量迁移
- 增量迁移
过程描述:
- 增量数据收集 (创建oracle表的增量物化视图)
- 进行全量复制
- 进行增量复制 (并行进行数据校验)
- 原库停写,切到新库
回滚方案:
- 开启新库到老库的数据回流
部署
下载
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. 如果白名单为空,代表整个库所有表,否则按指定的表进行同步 例子:
|
无 |
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
几点注意:
- linux启动完成后,会在bin目录下生成yugong.pid,stop.sh会读取yugong.pid进行进程关闭
- 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分钟后(保证延时的数据最终得到同步,此时源库和目标库当前数据是完全一致的)
- 检查增量持续处于NO_UPDATE状态,可关闭该迁移任务(sh stop.sh),即可发布新程序,使用新的数据库,完成切换的流程.
自定义数据转换
整个数据流为:DB -> Extractor -> DataTranslator -> Applier -> DB,本程序预留DataTranslator接口,允许外部用户自定义数据处理逻辑,比如:
- 表名不同
- 字段名不同
- 字段类型不同
- 字段个数不同
- 运行过程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);
}
}
几点说明:
- DataTranslator目前仅支持java扩展,允许用户完成类实现后,将类源文件放置到conf/translator/目录下,yugong启动后会进行动态编译.
- DataTranslator目前查找规则会根据表名自动查找,比如需要处理的表为otter2.test_all_one_pk,查找的时候会将test_all_one_pk转化为TestAllOnePk + 固定DataTranslator后缀. (如果当前classpath中存在,优先使用classpath,如果不存在,则到conf/translator中查找该名字的java文件进行动态编译)
- 目前提供了几个样例,可参见解压后的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)
全量模式,顾名思议即为对源表进行一次全量操作,遍历源表所有的数据后,插入目标表.
全量有两种处理方式:
- 分页处理:如果源表存在主键,只有一个主键字段,并且主键字段类型为Number类型,默认会选择该分页处理模式. 优点:支持断点续做,对源库压力相对较小。 缺点:迁移速度慢
- once处理:通过select * from访问整个源表的某一个mvcc版本的数据,通过cursor.next遍历整个结果集. 优点:迁移速度快,为分页处理的5倍左右。 缺点:源库压力大,如果源库并发修改量大,会导致数据库MVCC版本过多,出现栈错误. 还有就是不支持断点续做.
如果全量模式运行过程中,源库有变化时,不能保证源库最近变化的数据能同步到目标表,这时需要配合增量模式. 具体操作就是:在运行全量模式之前,先开启增量模式的记录日志功能,然后开启全量模式,完成后,再将最近变化的数据通过增量模式同步到目标表
增量模式(INC)
全量模式,顾名思议即为对源表增量变化的数据插入目标表,增量模式依赖记录日志功能.
目前增量模式的记录日志功能,是通过oracle的物化视图功能。
CREATE MATERIALIZED VIEW LOG ON ${tableName} with primary key.
- 运行增量模式之前,需要先开启记录日志的功能,即预先创建物化视图. 特别是配合全量模式时,创建物化视图的时间点要早于运行全量之前,这样才可以保证数据能全部同步到目标表
- 增量模式没有完成的概念,它只有追上的概念,具体的停止需有业务进行判断,可以看一下切换流程
自动模式(ALL)
自动模式,是对全量+增量模式的一种组合,自动化运行,减少操作成本.
自动模式的内部实现步骤:
- 开启记录日志功能. (创建物化视图)
- 运行全量同步模式. (全量完成后,自动进入下一步)
- 运行增量同步模式. (增量模式,没有完成的概念,所以也就不会自动退出,需要业务判断是否可以退出,可以看一下切换流程)
对比模式(CHECK)
对比模式,即为对源库和目标库的数据进行一次全量对比,验证一下迁移结果. 对比模式为一种可选运行,做完全量/增量/自动模式后,可选择性的运行对比模式,来确保本次迁移的正确性.
yugong诠释的更多相关文章
- 重新诠释的OSGi规范
上周五部门开会讨论新一代产品(基于.net Winform)的设计规范,从设计规范慢慢讨论到体系结构等架构存在的问题,诸如菜单.工具条.状态条.界面布局等不能实现配置化和自动化,子系统之间拥有强依赖, ...
- public static void main(String[] args){}函数诠释
public static void main(String[] args){}函数诠释 主函数的一般写法如下: public static void main(String[] args){-} 下 ...
- C++ Reference 的“三位一体”诠释
C++ 是介于汇编语言与高级语言之间的一种“全能”语言.它的能力是其他任何基于VMA(冯-诺曼架构)计算机的高级程序设计语言无法望其项背的,而性能也只有C语言可与之伯仲. 然而长期以来,喜欢C++和憎 ...
- HTML条件注释用法诠释
HTML条件注释用法诠释 注释内容以样式为例,如下: 1.支持所有IE浏览器 <!--[if IE]> <link rel="stylesheet" href=& ...
- Python笔记之 - 一张截图诠释"文件读写" !
Python笔记之 - 一张截图诠释"文件读写" ! 源代码如下: # 文件读写 str_test1 = "先创建txt文件再写入内容: 我是大帅哥" # wi ...
- Spring 配置 Annotation <context:annotation-config> 和 <context:component-scan>标签的诠释及区别
Spring 开启Annotation <context:annotation-config> 和 <context:component-scan>诠释及区别 <cont ...
- 立足“快时尚”,联想笋尖S90怎样诠释“比美更美”?
现现在."快时尚"已经成为广受年轻人追捧的消费观,从服装界的H&M.ZARA到餐饮界的绿茶.外婆家等等,我们都不难看出,快时尚已成为激发年轻人消费欲望的核心元素,并 ...
- 再谈mysql锁机制及原理—锁的诠释
加锁是实现数据库并发控制的一个非常重要的技术.当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁.加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更 ...
- 【ABAP系列】SAP ABAP诠释BDC的OK CODE含义
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP诠释BDC的OK ...
- 深度实战玩转算法, Java语言7个经典应用诠释算法精髓
深度实战玩转算法,以Java语言主讲,通过7款经典好玩游戏,真正将算法用于实际开发,由算法大牛ACM亚洲区奖牌获得者liuyubobobo主讲,看得见的算法,带领你进入一个不一样的算法世界,本套课程共 ...
随机推荐
- 在CIMES中调用存储过程
页面 调用步骤 DataTable dtResult = null; CustomDataAgent TODB_con = DBCenter.Create(AppSetting.Manufacturi ...
- uniapp 扫描
借鉴链接:https://blog.csdn.net/qq_33165549/article/details/89879435 1.扫描页面 <template> <view> ...
- react的生命周期和使用
完整的生命周期 我们都知道生命周期分为三个大阶段: 挂载 更新 卸载 挂载的时候我们我们有 constructor . getDerivedStateFromProps .render . compo ...
- Java语言出现的背景、影响及应用前景分析
一.背景 1991年 ,SUN MicroSystem公司的 Jame Gosling. Bill Joe等人 ,为在电视.控制烤面包箱等家用消费类电子产品上进行交互式操作而开发了一个名为Oak的软件 ...
- xlwings.copy两种用法和匹配超链接
第一种复制整个sheet页,不能覆盖同名sheet,需先删除.name新sheet也名称 after复制后的位置wb.sheets('增量机会-体外刷新导入').copy(name='快照版本(勿动) ...
- Python 集合常用方法
数据类型:int/str/bool/list/dict/tuple/float/set (set类型天生去重) 一.集合的定义 s = set() #定义空集合 s = {'a','b','c' ...
- Windows右键新建.md文件
原本创建.md文件的时候需要打开MarkDown编辑器,或者新建一个.txt文件然后修改后缀名,着实有点麻烦,本文介绍了如何在Windows操作系统中添加右键创建.md文件的方法. 环境 window ...
- ES6 - 参数默认值
1.形参初始值, 具有默认值的参数,一般位置要靠后(潜规则) function add(a,b,c=10){ return a + b + c; } let resutl = add(1,2); // ...
- 同一系统开启两个ftp进程
1.正常情况是yum 安装vsftpd服务 ,在/etc/vsftpd目录更改配置,起服务.这样启动是一个服务. 2.启动第二个vsftpd服务 #创建虚拟用户 useradd -d /home/vi ...
- MFC工程调用cJSON.c出现C1853错误的解决办法(老版本C文件加入新的C++项目)
环境 Visual Studio 2017 现象 头文件cJSON.h与源文件cJSON.c添加入工程后,编译出现如下C1853错误. cjson.c : fatal error C1853: &qu ...