yugong之單張表oracle到mysql遷移
阿里在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
解壓安裝
#解壓到安裝目錄
]# 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
注:上面紅色部分按照實際環境修改,這裡是測試而已,其他選項暫時都是預設的,具體的選項說明參考:
使用過程中,可以根據實際需要配置
配置資料轉換邏輯
如果要遷移的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;
#可以看到全量和後面增量的操作都已經同步
附: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開發人員 好像已經修復此問題
參考連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/27000195/viewspace-2072904/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- yugong之多張表oracle到mysql遷移GoOracleMySql
- MySQL 遷移表空間,備份單表MySql
- sqlldr 完成mysql到oracle的資料遷移MySqlOracle
- 1.0 ORACLE到MYSQL資料遷移方式選型OracleMySql
- Oracle分割槽表遷移Oracle
- oracle 表遷移方法 (一)Oracle
- 從MySQL到ORACLE程式遷移的注意事項(轉)MySqlOracle
- 遷移表到新的表空間
- mysql 大表mysqldump遷移方案MySql
- ORACLE表批量遷移表空間Oracle
- 藉助ogg完成oracle到mysql的資料遷移OracleMySql
- MySQL 資料遷移Oracle工作MySqlOracle
- 使用RMAN遷移單庫到RAC
- MySQL備份遷移之mydumperMySql
- mysql從一張表中取出資料插入到另一張表MySql
- oracle 遷移資料庫到asmOracle資料庫ASM
- 【STATS】Oracle遷移表統計資訊Oracle
- mysql遷移oracle週末計劃MySqlOracle
- 將表 從mysql 遷移到oracleMySqlOracle
- 使用RMAN簡單遷移表空間
- .NET框架下Oracle到SQL Server遷移框架OracleSQLServer
- Oracle中表空間、表、索引的遷移Oracle索引
- 異構資料庫資料遷移 oracle to mysql之oracle sqlloader和mysql load data資料庫OracleMySql
- 使用GoldenGate 遷移Oracle到PostgreSQL/LightDBGoOracleSQL
- oracle 資料遷移案例 從 8.1.7.4到9.2.0.8Oracle
- 從MySQL到Redis提升資料遷移的效率MySqlRedis
- oracle 表遷移方法 (二) 約束不失效Oracle
- Oracle 表空間資料檔案遷移Oracle
- Oracle 帶LOB欄位的表的遷移Oracle
- Oracle 不同平臺間表空間遷移Oracle
- 【遷移】SqlServer 遷移到 MySQL 方法ServerMySql
- mysql共享表空間擴容,收縮,遷移MySql
- mysql 恢復(one)資料庫及單張表MySql資料庫
- oracle 異構平臺遷移之傳輸表空間一例Oracle
- 一個簡單的MySQL資料遷移示例MySql
- 表空間遷移
- 遷移表空間
- DBMotion——MySQL遷移利器MySql