記一次簡單的Oracle離線數據遷移至TiDB過程
背景
最近在支援一個從Oracle轉TiDB的項目,為方便應用端兼容性測試需要把Oracle測試環境的庫表結構和數據同步到TiDB中,由於數據量並不大,所以怎麼方便怎麼來,這裡使用CSV導出導入的方式來實現。
整個過程可以分為三個步驟:
- 庫表結構轉換
- 源數據導出
- 導入目標庫
庫表結構轉換
眾所周知TiDB是兼容MySQL協議的,所以Oracle的表結構定義在TIDB不一定能完全使用,這時候就需要做一些轉換,比如欄位類型、關鍵字、系統函數等等。如果表比較少的話,手動轉一下也不是不行,但本次測試的Oracle其中一個用戶下就有將近900張表,手動去轉換顯然不可能。
這裡我使用的工具是TransferDB,它可以支援異構數據Oracle到MySQL/TiDB的結構轉換,項目主頁//github.com/wentaojin/transferdb。
這個工具由PingCAP某位大佬開發,雖然沒有正式對外發布,但確實挺好用的。TransferDB是TiDB運維常用工具集(TiDBA)中的一部分,其他的還包含收集統計資訊、Mok 解析 key、基於 region key、數據 range、數據估算生成打散語句、查看錶數據以及索引 region leader 分布、版本升級,比對 3.0 以及 4.0 配置文件以及 tidb 系統變數等,可以說是非常實用了,它的項目主頁是//github.com/wentaojin/tidba
使用過Lightning的朋友對這個工具的使用一定不會陌生,從配置文件到運行程式幾乎可以說是如出一轍,項目自帶的操作手冊也寫的非常詳細。
它包含以下幾點核心功能:schema轉換、表結構檢查、遷移成本評估、數據遷移(全量或增量)、CSV導出等,其中有些功能目前還是實驗特性,我這裡只用到了它的核心特性schema轉換。
它的配置文件參數非常豐富,注釋很清晰使用起來非常簡單,對於schema轉換場景來說,只需要修改[source]
和[target]
部分的連接資訊就行,詳細的配置清單可以看這裡://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/[email protected]: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/[email protected]: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的遷移案例://book.tidb.io/session4/chapter5/from-oracle-to-tidb.html,它基於Oracle自家的OGG組件來實現,部署使用上稍微有點麻煩。
本文提到的幾個工具都是隨取隨用,不需要安裝各種依賴環境,這對於我現在面對的這種沒有網路沒有yum源的伺服器來說簡直太舒服了,在這樣的小數據量測試場景中不失是一種選擇。
更多好方案也歡迎大家推薦,為TiDB生態助力。