数据同步

异构数据库迁移与同步(一)之yugong

2019-06-16  本文已影响0人  浩子淘天下

上次分享会已经介绍了Portal微服化的落地实践,前段时间主要是进行的去Oracle以及部分前端SPA化,本次分享主要介绍Portal在数据库从Oracle迁移到MySQL的实践经验。

应用场景

需求

基本流程

考量指标

迁移方案

停机数据迁移

停机迁移逻辑比较简单,使用ETL(Extract Translate Load)工具从 Source 写入 Target,然后进行一致性校验,最后确认应用运行OK,将 Source 表名改掉进行备份。

在线数据迁移

在线迁移的方案稍微复杂一些,流程上有准备全量数据,然后实时同步增量数据, 在数据同步跟上(延迟秒级别)之后,进行短暂停机(确保没有流量), 就可以使用新的应用配置,并使用新的数据库。

工具选型 - ETL工具&一致性校验工具&回滚工具

ETL的全称是 Extract Translate Load(读取、转换、载入),数据库迁移最核心过程就是ETL过程。 如果将ETL过程简化,去掉Translate过程,就退化为一个简单的数据导入导出工具。

MySQL同构数据库数据迁移工具

异构数据库迁移工具

ETL工具特性对比

由于本次迁移是异构数据迁移,能进入本次选型的只用:DataX / yugong / otter / DB2DB / MySQL Workbench。

一致性校验工具

在 ETL 之后,需要有一个流程来确认数据迁移前后是否一致。 虽然理论上不会有差异,但是如果中间有程序异常, 或者数据库在迁移过程中发生操作,数据就会不一致。

回滚

在系统迁移完成后,一段时间后遇到了一些 Critical 级别的问题,必须回滚到迁移之前状态。

初识yugong

简介

架构

1、一个Jvm Container对应多个instance,每个instance对应于一张表的迁移任务
2、instance分为三部分: a. extractor (从源数据库上提取数据,可分为全量/增量实现) b. translator (将源库上的数据按照目标库的需求进行自定义转化) c. applier (将数据更新到目标库,可分为全量/增量/对比的实现)

设计方案

常用方案:

  1、数据文件导入/导出,比如EXPDP/IMPDP, mysqldump/source, xtrabackup等;        2、ETL数据导入/导出,主要原理为使用JDBC数据查询接口   

考虑到数据迁移的==灵活性==和==自定义能力==最终选择基于JDBC接口遍历数据。

基于JDBC 优点:

1、灵活数据同步2、支持异构数据3、实现相对简单

缺点:

1、全量拉取需要配合增量使用,会有部分数据重复同步2、性能和影响,一次性全量拉取,如果持续时间过长,如果此时数据库变更过多,会导致segment过大

常用方案:

1、基于时间戳定时dump2、oracle日志文件,比如LogMiner,OGG3、oracle CDC(Change Data Capture)4、oracle trigger机制,比如DataBus , SymmetricDS5、oracle 物化视图(materialized view)6、...

考虑去IOE数据迁移的灵活性,支持多种oracle版本,同时为降低DBA的运维成本,yugong选择了oracle物化视图作为增量方案。

物化视图方案 优点:

1、原理简单,方便理解和学习,可理解为一种固化的简易trigger模式2、运维简单,DBA一次账户授权后,程序可按需create一张物化视图表即可完成增量订阅3、相对透明,不需要像时间戳sql扫描依赖数据库表设计,也不需要关注oracle版本和服务器存储等

缺点:

  1. 1、性能和影响,类似于trigger机制会对源库的数据写入造成一定的性能影响.

yugong小试

数据库

  1. 源库(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;
  1. 目标库(mysql/oracle)

  1. GRANT SELECT,INSERT,UPDATE,DELETE ON XXX TO XXX;

目录结构

drwxr-xr-x 2 root root  4096 7月  23 13:18 bindrwxr-xr-x 6 root root  4096 7月  23 12:59 conf  -    -rwxrwxrwx 1 root root  4700 4月   1 2016 logback.xml    drwxrwxrwx 2 root root  4096 7月  21 09:43 positioner ##同步的位置信息    drwxr-xr-x 2 root root  4096 7月  21 09:30 translator      -        -rw-r--r-- 1 root root 4266 7月   4 11:14 PortalResourceDataTranslator.java        -rw-r--r-- 1 root root 3174 7月   4 10:40 PortalRoleDataTranslator.java        -rw-r--r-- 1 root root 2633 7月   4 10:57 PortalRoleToResourceDataTranslator.java        -rw-r--r-- 1 root root 2617 7月   4 10:54 PortalUserToRoleDataTranslator.javadrwxr-xr-x 2 root root  4096 7月  23 12:56 libdrwxr-xr-x 7 root root  4096 7月  23 12:56 logs

修改配置

yugong.database.source.username=portalyugong.database.source.password=******yugong.database.source.type=ORACLEyugong.database.source.url=jdbc:oracle:thin:@192.168.1.1:1521:PORTALyugong.database.source.encode=UTF-8yugong.database.source.poolSize=30yugong.database.target.url=jdbc:mysql://192.168.1.1:3306/portalyugong.database.target.username=portalyugong.database.target.password=****yugong.database.target.type=MYSQLyugong.database.target.encode=UTF-8yugong.database.target.poolSize=30yugong.table.batchApply=trueyugong.table.onceCrawNum=1000yugong.table.tpsLimit=0# use connection default schemayugong.table.ignoreSchema=false# skip Applier Load Db failed datayugong.table.skipApplierException=false#yugong.table.white=yugong_example_join,yugong_example_oracle,yugong_example_twoyugong.table.white=PORTAL_ROLE,PORTAL_RESOURCE,PORTAL_USER_TO_ROLE,PORTAL_ROLE_TO_RESOURCEyugong.table.black=# tables use multi-thread enable or disableyugong.table.concurrent.enable=true# tables use multi-thread sizeyugong.table.concurrent.size=23# 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/CLEARyugong.table.mode=ALL# yugong extractoryugong.extractor.dump=falseyugong.extractor.concurrent.enable=trueyugong.extractor.concurrent.global=falseyugong.extractor.concurrent.size=30yugong.extractor.noupdate.sleep=1000yugong.extractor.noupdate.thresold=0yugong.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 applieryugong.applier.concurrent.enable=trueyugong.applier.concurrent.global=falseyugong.applier.concurrent.size=30yugong.applier.dump=false# statsyugong.stat.print.interval=5yugong.progress.print.interval=1# alarm emailyugong.alarm.email.host = smtp.163.comyugong.alarm.email.username = test@163.comyugong.alarm.email.password = yugong.alarm.email.stmp.port = 465yugong.alarm.receiver=test@163.com

启动停止

别懒,自己探索吧。

查看日志

目录

logs/  - yugong/  #系统根日志     - table.log  - ${table}/  #每张同步表的日志信息     - table.log     - extractor.log     - applier.log     - check.log

全量完成的日志:(在yugong/table.log 和 ${table}/table.log中记录)

  1. table[PORTAL.PORTAL_RESOURCE] is end!

增量日志:(在${table}/table.log中记录)

table[PORTAL.PORTAL_RESOURCE] now is CATCH_UP ... #代表已经追上,最后一次增量数据小于onceCrawNum数量table[PORTAL.PORTAL_RESOURCE] now is NO_UPDATE ... #代表最近一次无增量数据

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

  1. table [PORTAL.PORTAL_RESOURCE] 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]

统计信息:

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

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

切换流程

  1. 当任务处于追上状态时候,表示已经处于实时同步状态

  2. 后续通过源数据库进行停写,稍等1-2分钟后(保证延时的数据最终得到同步,此时源库和目标库当前数据是完全一致的)

  3. 检查增量持续处于NO_UPDATE状态,可关闭该迁移任务,即可升级新程序,使用新MySQL库,完成切换的流程。

自定义数据转换

如果要迁移的oracle和mysql的表结构不同,比如表名,字段名有差异,字段类型不兼容,需要使用自定义数据转换。数据流:DB -> Extractor -> DataTranslator -> Applier -> DB,可实现==DataTranslator接口==来处理数据逻辑。

举例

public class PortalResourceDataTranslator extends AbstractDataTranslator implements DataTranslator {    public boolean translator(Record record) {        // 1. schema/table名不同        record.setSchemaName("portal");        record.setTableName("author_resource");        // 2. 字段名字不同        ColumnValue c1  = record.getColumnByName("ID");        if (c1 != null) {            c1.getColumn().setName("id");        }        ColumnValue c2 = record.getColumnByName("ACCESS_MODULE");        if (c2 != null) {            c2.getColumn().setName("access_module");        }        ColumnValue c3 = record.getColumnByName("ACCESS_PATH");        if (c3 != null) {            c3.getColumn().setName("access_path");        }        // 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);        return super.translator(record);    }}/**疑问:1、DataTranslator动态编译2、DataTranslator查找规则:根据表名自动查找3、其他复杂转换:    a、多张Oracle表和一张MySQL转换处理    b、一张Oracle表和多张MySQL**/

运行模式细谈

Portal自助配置在线迁移

准备工作

迁移流程

迁移疑问

采坑记录

本文作者:haozi
原文链接:http://blog.chuangzhi8.cn/2018/08/19/异构数据库迁移与同步-一-之yugong/
版权归作者所有,转载请注明出处

捅一下

上一篇 下一篇

猜你喜欢

热点阅读