背景
最近在支援一個從Oracle轉TiDB的專案,為方便應用端相容性測試需要把Oracle測試環境的庫表結構和資料同步到TiDB中,由於資料量並不大,所以怎麼方便怎麼來,這裡使用CSV匯出匯入的方式來實現。
整個過程可以分為三個步驟:
- 庫表結構轉換
- 源資料匯出
- 匯入目標庫
庫表結構轉換
眾所周知TiDB是相容MySQL協議的,所以Oracle的表結構定義在TIDB不一定能完全使用,這時候就需要做一些轉換,比如欄位型別、關鍵字、系統函式等等。如果表比較少的話,手動轉一下也不是不行,但本次測試的Oracle其中一個使用者下就有將近900張表,手動去轉換顯然不可能。
這裡我使用的工具是TransferDB,它可以支援異構資料Oracle到MySQL/TiDB的結構轉換,專案主頁https://github.com/wentaojin/transferdb。
這個工具由PingCAP某位大佬開發,雖然沒有正式對外發布,但確實挺好用的。TransferDB是TiDB運維常用工具集(TiDBA)中的一部分,其他的還包含收集統計資訊、Mok 解析 key、基於 region key、資料 range、資料估算生成打散語句、檢視錶資料以及索引 region leader 分佈、版本升級,比對 3.0 以及 4.0 配置檔案以及 tidb 系統變數等,可以說是非常實用了,它的專案主頁是https://github.com/wentaojin/tidba
使用過Lightning的朋友對這個工具的使用一定不會陌生,從配置檔案到執行程式幾乎可以說是如出一轍,專案自帶的操作手冊也寫的非常詳細。
它包含以下幾點核心功能:schema轉換、表結構檢查、遷移成本評估、資料遷移(全量或增量)、CSV匯出等,其中有些功能目前還是實驗特性,我這裡只用到了它的核心特性schema轉換。
它的配置檔案引數非常豐富,註釋很清晰使用起來非常簡單,對於schema轉換場景來說,只需要修改[source]
和[target]
部分的連線資訊就行,詳細的配置清單可以看這裡:https://github.com/wentaojin/transferdb/blob/main/conf/config.toml
配置檔案修改好以後,執行下面兩條命令就可以實現轉換:
# 這個過程是在目標庫中生成一個遷移元資訊庫,用來儲存轉換規則、斷點資訊等等,類似於DM中的dm_meta庫
./transferdb --config config.toml --mode prepare
# 這個過程是實現schema轉換,輸出sql檔案
./transferdb --config config.toml --mode reverse
執行成以後會生成2個SQL檔案,一個叫 reverse_${sourcedb}.sql
,它是在TiDB中可以執行的sql,另一個是 compatibility_${sourcedb}.sql
,它是TiDB不相容的sql,比如Foreign Key、Constraint等等,這部分SQL需要人工去評估下使用別的方案來實現。
接著,把reverse_${sourcedb}.sql
匯入到TiDB即可,常用的兩種方式:
- mysql -h -u -P < reverse.sql
- source reverse.sql
源資料匯出
Oracle資料匯出到CSV檔案我使用sqluldr2來實現,這是一款在Oracle使用非常廣泛的資料匯出工具,它的特點就是小巧、輕便、速度快、跨平臺、支援自定義SQL。
網上的資料比較多,這裡就不詳細介紹怎麼去使用了,作者(前阿里資料庫大佬)也寫了一份超級詳細的文件,大家搜尋sqluldr2超詳細使用教程-loracle資料匯出工具及方法
即可。
sqluldr2雖然很強大,但它卻不支援批量匯出這點很讓人迷惑,沒辦法只能另闢蹊徑來實現了。
我先把需要匯出的表清單放到一個txt檔案中:
./sqluldr2linux64.bin user=user/pwd@192.168.1.1:1521/orcl query='select table_name from all_tables where owner='test';' file=/tmp/tidb/sqluldr_tables.sql
再寫一個批處理指令碼把所有表進行匯出:
#!/bin/bash
cat /tmp/tidb/sqluldr_tables.sql | while read line
do
echo $line
/tmp/tidb/sqluldr2linux64.bin user=user/pwd@192.168.1.1:1521/orcl query={$line} charset=UTF8 field=0x7c0x260x7c record=0x3d0x37 null=null file=/tmp/tidb/data/orcltest.{$line}.csv
done
這裡有幾點需要注意:
- 欄位分隔符和換行符建議使用複雜的字元,最好使用多種組合字元(推薦使用ASCII碼),這樣能儘可能的避免出現匯出的資料value count和column count不一致的情況。
- 匯出的檔案字符集設定成UTF8格式,避免資料匯入到TiDB後出現中文亂碼
- 由於後面要使用Lightning匯入CSV,檔案命名格式要符合Lightning的要求,即{dbname}.{tablename}.csv
到這裡資料就準備就緒了。
匯入到TiDB
往TiDB中匯入CSV檔案有兩種常用的方式,第一種就是Lightning,第二種是Load Data,無論是從操作上還是效能上我都推薦優先考慮Lightning,原因如下:
- Load Data只能匯入單個檔案,Lightning可以批量匯入
- Lightning以效能著稱,支援多種後端模式,Load Data只能走計算層,還有記憶體放大現象
- 對於異常情況,Lightning可以斷點續傳,Load Data要清理資料重新匯入
單從900個csv檔案來看,Lightning絕對是第一選擇。
這裡貼一下Lightning的幾項核心配置:
[tikv-importer]
# 選擇使用的 local 後端
backend = "local"
# 設定排序的鍵值對的臨時存放地址,目標路徑需要是一個空目錄
sorted-kv-dir = "/tmp/tidb/lightning_dir"
[mydumper]
data-source-dir = "/tmp/tidb/data"
no-schema = true
filter = ['*.*']
[mydumper.csv]
# 欄位分隔符,支援一個或多個字元,預設值為 ','。
separator = '|&|'
# 引用定界符,設定為空表示字串未加引號。
delimiter = ''
# 行尾定界字元,支援一個或多個字元。設定為空(預設值)表示 "\n"(換行)和 "\r\n" (回車+換行),均表示行尾。
terminator = "=%"
# CSV 檔案是否包含表頭。
# 如果 header = true,將跳過首行。
header = false
# CSV 檔案是否包含 NULL。
# 如果 not-null = true,CSV 所有列都不能解析為 NULL。
not-null = false
# 如果 not-null = false(即 CSV 可以包含 NULL),
# 為以下值的欄位將會被解析為 NULL。
null = '\N'
# 是否對欄位內“\“進行轉義
backslash-escape = true
# 如果有行以分隔符結尾,刪除尾部分隔符。
trim-last-separator = false
注意事項:
- 推薦使用local模式,這樣應對blob型別的資料處理更友好
- 不需要匯入表結構,所以設定no-schema = true
- 分隔符和換行符這些要和sqluldr2設定的一樣
最後執行Lightning即可:
./tidb-lightning --config tidb.toml --checkrequirements=false
這個過程中我還發現了一個Lightning的bug,後面我會嘗試自己修復一下。
其他部分
至於Oracle的其他部分例如儲存過程和自定義函式,也不用多想怎麼去遷移了,老老實實改程式碼去應用端實現。
檢視的話我是先用PLSQL把檢視導成sql檔案再匯入TiDB中,但是報錯的比較多,基本都是系統函式和語法不相容問題,這部分就涉及SQL改寫了,沒有什麼好的辦法。
總結
在之前的專案中也使用過DSG來實現Oracle到TiDB的資料遷移,但它畢竟是個純商業性工具,先充錢才能玩,對於這種測試階段來說不是太好的選擇。
當然了,官方釋出的《TiDB in Action》一書中也給出了Oracle到TiDB的遷移案例:https://book.tidb.io/session4/chapter5/from-oracle-to-tidb.html,它基於Oracle自家的OGG元件來實現,部署使用上稍微有點麻煩。
本文提到的幾個工具都是隨取隨用,不需要安裝各種依賴環境,這對於我現在面對的這種沒有網路沒有yum源的伺服器來說簡直太舒服了,在這樣的小資料量測試場景中不失是一種選擇。
更多好方案也歡迎大家推薦,為TiDB生態助力。