據(jù)同步實(shí)戰(zhàn):從MySQL到HDFS的配置解析與性能調(diào)優(yōu))
1. 內(nèi)容整體設(shè)計(jì)與思路拆解1.1 為什么選擇 DataX 而不是其他同步工具我一直在做數(shù)據(jù)平臺(tái)的活兒日常打交道最多的就是各種數(shù)據(jù)遷移、離線同步、倉(cāng)庫(kù)存取。最開(kāi)始那幾年同步數(shù)據(jù)基本靠寫Shell腳本加SQL導(dǎo)出導(dǎo)入或者用Sqoop這種老牌工具。但用著用著就發(fā)現(xiàn)問(wèn)題了腳本一多維護(hù)成本直線上升跑掛了沒(méi)人發(fā)現(xiàn)數(shù)據(jù)類型稍微復(fù)雜一點(diǎn)就開(kāi)始鬧脾氣而且不同數(shù)據(jù)源之間的同步方式完全不統(tǒng)一每接一個(gè)新源就要重寫一套邏輯。后來(lái)接觸到DataX最大的感受就是這玩意兒把“數(shù)據(jù)同步”這件事抽象得特別干凈。它不需要你部署服務(wù)端就是一個(gè)獨(dú)立運(yùn)行的進(jìn)程你給它一個(gè)JSON格式的作業(yè)配置它就能把數(shù)據(jù)從源頭拉到目的地。不像有些同步工具要裝Agent、要搭管理端DataX本身就是個(gè)“跑完即走”的命令行工具特別適合那種不想引入太重基礎(chǔ)設(shè)施的場(chǎng)景。我選擇DataX還有幾個(gè)很現(xiàn)實(shí)的原因第一它支持的數(shù)據(jù)源特別全MySQL、Oracle、SQL Server、HDFS、Hive、HBase、MongoDB、Elasticsearch、ClickHouse這些常見(jiàn)的都覆蓋到了而且還在持續(xù)更新。第二它的運(yùn)行機(jī)制是“讀取-緩沖-寫入”三段式理論上任意兩個(gè)數(shù)據(jù)源之間都能通過(guò)配置組合實(shí)現(xiàn)同步。第三它是純Java寫的跨平臺(tái)跑沒(méi)問(wèn)題扔到服務(wù)器上就能用。如果你只是偶爾同步幾張表或者需要在不穩(wěn)定的網(wǎng)絡(luò)環(huán)境下做斷點(diǎn)重跑DataX這種輕量級(jí)工具其實(shí)比調(diào)度平臺(tái)自帶的同步組件更靈活因?yàn)樗鼘?duì)運(yùn)行環(huán)境的要求極低——只要有JDK有Python執(zhí)行入口腳本用就能跑起來(lái)。1.2 一次同步任務(wù)的基本運(yùn)行邏輯很多人第一次接觸DataX時(shí)會(huì)被它的目錄結(jié)構(gòu)嚇到其實(shí)拆開(kāi)看就兩件事寫配置、跑命令。一個(gè)完整的DataX同步任務(wù)本質(zhì)上是定義了一條數(shù)據(jù)流轉(zhuǎn)管道。你的核心工作就是寫好一個(gè)JSON文件里面描述清楚三件事數(shù)據(jù)從哪來(lái)reader、數(shù)據(jù)寫到哪去writer、以什么速率跑channel。DataX會(huì)解析這個(gè)JSON啟動(dòng)一個(gè)調(diào)度進(jìn)程把任務(wù)拆分成多個(gè)并發(fā)線程通過(guò)內(nèi)存緩沖區(qū)把數(shù)據(jù)從源頭搬運(yùn)到目標(biāo)端。跑任務(wù)的命令也特別簡(jiǎn)單python datax.py job.json就這一行沒(méi)有了。所有復(fù)雜的連接信息、字段映射、并發(fā)控制全部沉淀在JSON配置里。這也意味著你的所有“功力”都在于怎么把JSON寫好、寫對(duì)。我自己第一次跑通DataX時(shí)花了不少時(shí)間在“JSON怎么寫才合法”上面。它有一套固定的結(jié)構(gòu)比如最外層是jobjob下面是contentcontent里面是一個(gè)數(shù)組每個(gè)元素包含reader、writer、setting。這個(gè)結(jié)構(gòu)看起來(lái)簡(jiǎn)單但嵌套層級(jí)一旦多起來(lái)少個(gè)括號(hào)、多個(gè)逗號(hào)任務(wù)就直接報(bào)錯(cuò)。所以后來(lái)我習(xí)慣了寫完之后用Python的json.tool先校驗(yàn)一遍再做真實(shí)執(zhí)行。2. 環(huán)境準(zhǔn)備與安裝部署2.1 最小化環(huán)境依賴DataX對(duì)運(yùn)行環(huán)境的要求確實(shí)很低但“低”不代表“不用準(zhǔn)備”。我實(shí)踐下來(lái)的最小環(huán)境是JDK 1.8及以上版本推薦直接裝OpenJDK。Python 2.7或3.x用來(lái)啟動(dòng)datax.py腳本。實(shí)測(cè)Python 3.6以上也沒(méi)問(wèn)題。目標(biāo)數(shù)據(jù)源和源數(shù)據(jù)源各自的連接權(quán)限、賬號(hào)信息、網(wǎng)絡(luò)連通性。有足夠的磁盤空間DataX會(huì)使用臨時(shí)目錄存放運(yùn)行時(shí)產(chǎn)生的中間文件。安裝就更簡(jiǎn)單了把DataX壓縮包扔到服務(wù)器上解壓之后目錄結(jié)構(gòu)長(zhǎng)這樣datax/ bin/ datax.py conf/ core.json logback.xml job/ job.json lib/ datax-core-xxx.jar datax-common-xxx.jar ... 各種數(shù)據(jù)源插件jar包 plugin/ reader/ mysqlreader/ hdfsreader/ ... writer/ mysqlwriter/ hdfswriter/ ... tmp/ log/plugin目錄才是DataX的靈魂。每個(gè)數(shù)據(jù)源插件都是獨(dú)立打包的互不干擾。如果你只需要MySQL到HDFS的同步甚至可以把不需要的插件刪掉體積能瘦身不少啟動(dòng)速度也會(huì)快一點(diǎn)。不過(guò)新手不建議亂刪等完全理解了每個(gè)插件的依賴關(guān)系之后再做精簡(jiǎn)。2.2 環(huán)境變量配置與啟動(dòng)驗(yàn)證解壓之后推薦把DataX的目錄配到PATH里。但我一般不愛(ài)改全局環(huán)境變量因?yàn)榉?wù)器上可能有多個(gè)版本共存的情況。我習(xí)慣在需要執(zhí)行任務(wù)的用戶目錄下的.bashrc文件里寫export DATAX_HOME/opt/datax export PATH$PATH:$DATAX_HOME/bin這樣在任何目錄下都能直接敲datax.py命令。配置完成之后先跑一下自帶的自檢腳本確認(rèn)基礎(chǔ)環(huán)境沒(méi)問(wèn)題python $DATAX_HOME/bin/datax.py $DATAX_HOME/job/job.json如果能看到“任務(wù)啟動(dòng)時(shí)刻”“任務(wù)結(jié)束時(shí)刻”“任務(wù)總計(jì)耗時(shí)”“平均流量”這些信息輸出說(shuō)明安裝成功可以開(kāi)始正式寫任務(wù)了。提示如果你的服務(wù)器是內(nèi)網(wǎng)環(huán)境沒(méi)有外網(wǎng)那就不能使用在線安裝的方式只能先把安裝包傳到服務(wù)器上解壓。注意確認(rèn)JDK的位數(shù)和版本出現(xiàn)過(guò)不少因?yàn)镴DK版本太高導(dǎo)致部分插件加載不了的情況保守點(diǎn)直接用JDK 8最穩(wěn)。3. 核心配置解析與JSON結(jié)構(gòu)拆解3.1 一個(gè)標(biāo)準(zhǔn)任務(wù)JSON的完整框架先給一個(gè)我日常用得最多的模板MySQL同步到MySQL{ job: { content: [ { reader: { name: mysqlreader, parameter: { username: root, password: 123456, column: [*], connection: [ { table: [t_user], jdbcUrl: [jdbc:mysql://localhost:3306/test?useSSLfalsecharacterEncodingutf8] } ] } }, writer: { name: mysqlwriter, parameter: { username: root, password: 123456, writeMode: insert, column: [*], connection: [ { table: [t_user_bak], jdbcUrl: jdbc:mysql://localhost:3306/test?useSSLfalsecharacterEncodingutf8 } ] } } } ], setting: { speed: { channel: 3, byte: 1048576 } } } }這個(gè)JSON是理解所有DataX配置的鑰匙。不管你后面用多少個(gè)Reader、多少個(gè)Writer底層表達(dá)方式完全一致。你只需要替換name字段和parameter里各個(gè)數(shù)據(jù)源專屬的連接參數(shù)其他框架完全不用動(dòng)。3.2 Reader和Writer的參數(shù)細(xì)節(jié)Reader部分的重點(diǎn)在三個(gè)地方怎么連接源端、讀哪些列、以及并發(fā)切片機(jī)制。column字段既可以是通配符*也可以寫具體的列名列表。我建議生產(chǎn)環(huán)境永遠(yuǎn)不要用*要顯式列出列名。一方面防止源端表結(jié)構(gòu)變更導(dǎo)致同步出錯(cuò)另一方面DataX在做字段映射時(shí)按名稱匹配比按位置匹配更穩(wěn)。connection下的table支持?jǐn)?shù)組意味著你可以一次配置多張表。但要小心多表配置時(shí)DataX會(huì)按表順序逐個(gè)讀取如果各表結(jié)構(gòu)不一致很可能報(bào)錯(cuò)。我習(xí)慣一次只同步一張表多張表就多寫幾個(gè)JSON配合調(diào)度平臺(tái)并行跑。Writer部分的重點(diǎn)在writeMode。以mysqlwriter為例主要有三個(gè)取值insert直接插入、update按主鍵更新、replace存在則替換不存在則插入。這個(gè)選擇直接決定了同步的冪等性策略。如果你做的是全量覆蓋同步推薦用replace配合JobId能實(shí)現(xiàn)類似“按批次清理數(shù)據(jù)”的效果。如果是增量同步就用insert。還有一點(diǎn)容易被忽略Writer的jdbcUrl字段值是一個(gè)字符串而Reader的jdbcUrl字段值是一個(gè)數(shù)組。這個(gè)差異官方文檔里寫得很隱晦我當(dāng)初調(diào)試了半天才發(fā)現(xiàn)是類型不匹配的問(wèn)題。DataX的多個(gè)Reader插件和Writer插件之間參數(shù)格式并不完全對(duì)稱寧可先看官方示例再改。3.3 Channel參數(shù)如何決定同步速率setting.speed.channel是控制并發(fā)度的核心參數(shù)。它是怎么運(yùn)作的呢DataX會(huì)把Reader讀到的數(shù)據(jù)放進(jìn)內(nèi)存隊(duì)列Writer從隊(duì)列里取數(shù)據(jù)寫入目標(biāo)端。channel的數(shù)量相當(dāng)于同時(shí)跑了多少條讀寫管道。channel越多并行度越高同步速度越快但同時(shí)也意味著更高的內(nèi)存消耗、更大的源端和目的端壓力。我整理過(guò)一組實(shí)際經(jīng)驗(yàn)值供你參考場(chǎng)景channel數(shù)說(shuō)明小表幾十萬(wàn)行以內(nèi)1避免頻繁建連夠用即可中等規(guī)模千萬(wàn)級(jí)3-5兼顧速度和穩(wěn)定性大表億級(jí)5-10需要壓測(cè)確認(rèn)源端扛得住極端快速導(dǎo)入15以上只建議在內(nèi)網(wǎng)高帶寬場(chǎng)景使用另外channel到底是多少不能只看數(shù)據(jù)量還要看源端數(shù)據(jù)庫(kù)的負(fù)載。我遇到過(guò)把channel調(diào)到10之后直接把源端MySQL的CPU打滿的情況。那是一個(gè)業(yè)務(wù)核心庫(kù)差點(diǎn)出事。所以我現(xiàn)在調(diào)并發(fā)時(shí)會(huì)遵循“逐步遞增”的原則先用channel1跑一遍記錄耗時(shí)和流量再把channel翻倍觀察數(shù)據(jù)庫(kù)監(jiān)控指標(biāo)穩(wěn)定之后再繼續(xù)往上加直到性能不再提升反而下降的那個(gè)點(diǎn)取上一個(gè)值作為最終配置。setting.speed.byte參數(shù)是流控用的如果不設(shè)置DataX會(huì)盡可能跑滿帶寬。對(duì)于生產(chǎn)任務(wù)建議設(shè)置一個(gè)合理的上限防止同步任務(wù)占滿網(wǎng)絡(luò)帶寬影響線上業(yè)務(wù)。比如設(shè)置1048576單位是字節(jié)就是每秒最多1MB加上channel機(jī)制實(shí)際上最終速率是兩者取較小值這個(gè)不要搞混了。4. 實(shí)操過(guò)程從零跑通一個(gè)同步任務(wù)4.1 場(chǎng)景設(shè)定MySQL數(shù)據(jù)同步到HDFS為了把一個(gè)完整的實(shí)操過(guò)程講清楚我拿一個(gè)真實(shí)場(chǎng)景舉例把業(yè)務(wù)庫(kù)MySQL里的用戶訂單表同步到HDFS上按天分區(qū)存放做離線數(shù)倉(cāng)的原始數(shù)據(jù)層。目標(biāo)源表業(yè)務(wù)庫(kù)testdb下的t_order目標(biāo)HDFS路徑 /warehouse/ods/t_order/dt20250115文件格式TextFile字段分隔符設(shè)為\t同步方式當(dāng)天全量快照先看一下最終的job.json長(zhǎng)什么樣{ job: { content: [ { reader: { name: mysqlreader, parameter: { username: readonly, password: xxxx, column: [ order_id, user_id, product_id, order_amount, order_status, create_time ], splitPk: order_id, connection: [ { table: [t_order], jdbcUrl: [jdbc:mysql://10.0.0.x:3306/testdb?useSSLfalsecharacterEncodingutf8zeroDateTimeBehaviorconvertToNull] } ] } }, writer: { name: hdfswriter, parameter: { defaultFS: hdfs://10.0.0.y:9000, fileType: text, path: /warehouse/ods/t_order/dt20250115, fileName: t_order_, column: [ { name: order_id, type: long }, { name: user_id, type: long }, { name: product_id, type: long }, { name: order_amount, type: double }, { name: order_status, type: string }, { name: create_time, type: string } ], writeMode: append, fieldDelimiter: \t, compress: gzip } } } ], setting: { speed: { channel: 4, byte: 52428800 }, errorLimit: { record: 0, percentage: 0 } } } }4.2 關(guān)鍵參數(shù)的設(shè)計(jì)邏輯splitPk這個(gè)參數(shù)很關(guān)鍵。它告訴DataX用哪個(gè)字段作為分片鍵來(lái)切分并發(fā)任務(wù)。如果設(shè)置了splitPkDataX會(huì)在底層把一張表的數(shù)據(jù)按照這個(gè)字段的范圍切成多段讓多個(gè)channel并行讀取。不設(shè)置的話即使channel配置了多個(gè)也只會(huì)在數(shù)據(jù)源層面做JDBC連接復(fù)用沒(méi)法真正并行拉取。splitPk通常選擇主鍵或數(shù)字類型的字段。因?yàn)镈ataX底層是用類似于between and的條件去分段讀取數(shù)據(jù)varchar類型也可以但需要確認(rèn)數(shù)據(jù)庫(kù)端能做范圍查詢優(yōu)化。我實(shí)測(cè)下來(lái)用自增主鍵效果最好切分均勻查詢計(jì)劃也穩(wěn)定。hdfswriter的path路徑我寫的是具體到日期的分區(qū)路徑。這跟Hive數(shù)倉(cāng)的分區(qū)表結(jié)構(gòu)是對(duì)應(yīng)的后續(xù)如果要用Hive查數(shù)據(jù)直接通過(guò)分區(qū)路徑就能映射到分區(qū)字段。還有一點(diǎn)要說(shuō)明DataX在寫入HDFS時(shí)不會(huì)自動(dòng)創(chuàng)建多級(jí)目錄所以你需要提前用hdfs dfs -mkdir -p把目錄建好或者依賴調(diào)度平臺(tái)在跑任務(wù)前執(zhí)行建目錄操作。writeModeappend表示追加寫入不覆蓋已有文件。對(duì)于每天同步的場(chǎng)景這個(gè)模式是安全的因?yàn)槊刻斓哪夸浭仟?dú)立的不存在覆蓋問(wèn)題。如果同步的是業(yè)務(wù)上要覆蓋的維度表可以考慮用truncate模式它會(huì)先清空目錄再寫入。4.3 從執(zhí)行到驗(yàn)證的完整流程寫好JSON后先校驗(yàn)格式python -m json.tool job.json沒(méi)有報(bào)錯(cuò)說(shuō)明語(yǔ)法沒(méi)問(wèn)題再執(zhí)行python /opt/datax/bin/datax.py job.json執(zhí)行過(guò)程中會(huì)實(shí)時(shí)輸出每個(gè)channel的讀取速度、寫入速度、流量信息。我習(xí)慣盯著兩個(gè)指標(biāo)看讀取記錄總數(shù)確認(rèn)跟源表行數(shù)一致??梢栽谠磶?kù)跑一條count(*)兩邊核對(duì)。運(yùn)行速度是否達(dá)到預(yù)期如果長(zhǎng)時(shí)間在幾十KB/s徘徊多半是數(shù)據(jù)庫(kù)端慢查詢或者網(wǎng)絡(luò)有瓶頸。跑完之后的輸出片段大致是這樣任務(wù)啟動(dòng)時(shí)刻: 2025-01-15 01:00:00 任務(wù)結(jié)束時(shí)刻: 2025-01-15 01:08:32 任務(wù)總計(jì)耗時(shí): 512s 任務(wù)平均流量: 18.67MB/s 記錄寫入速度: 32,500rec/s 讀出記錄總數(shù): 16,658,432 讀寫失敗總數(shù): 0看到讀寫失敗總數(shù)為0就可以收工了。下一步是去HDFS上看一眼文件是否正常生成hdfs dfs -ls /warehouse/ods/t_order/dt20250115/ hdfs dfs -cat /warehouse/ods/t_order/dt20250115/* | head -10我一般還會(huì)順手跑一下文件行數(shù)統(tǒng)計(jì)用wc -l或者Hive里的count對(duì)比一下源表的行數(shù)確保兩邊一致。這一步看似多余但在真實(shí)生產(chǎn)里確實(shí)抓到過(guò)幾次數(shù)據(jù)條數(shù)對(duì)不上的問(wèn)題原因都是源端表在同步過(guò)程中有并發(fā)寫入。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 連接超時(shí)與連接被拒最常見(jiàn)的坑就是數(shù)據(jù)源連不上。報(bào)錯(cuò)信息一般長(zhǎng)這樣Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure我的排查順序是固定的先在本機(jī)用mysql命令測(cè)試一下能不能連上目標(biāo)庫(kù)排除網(wǎng)絡(luò)問(wèn)題。檢查jdbcUrl里的IP和端口是否真的開(kāi)放了。很多時(shí)候是安全組策略沒(méi)放行這個(gè)在云上環(huán)境尤其常見(jiàn)。確認(rèn)賬號(hào)權(quán)限沒(méi)有問(wèn)題。DataX連接數(shù)據(jù)庫(kù)的賬號(hào)需要有SELECT權(quán)限寫端還要有INSERT、UPDATE、DELETE、CREATE等權(quán)限缺一不可。檢查數(shù)據(jù)庫(kù)的max_connections有沒(méi)有打滿。如果數(shù)據(jù)庫(kù)連接池已經(jīng)被占滿DataX的連接就會(huì)被拒。還有一個(gè)容易忽視的細(xì)節(jié)MySQL連接串里要帶useSSLfalse。內(nèi)網(wǎng)數(shù)據(jù)庫(kù)一般都沒(méi)開(kāi)SSL如果這里不寫連接初始化階段會(huì)花很長(zhǎng)時(shí)間甚至直接hang住。5.2 數(shù)據(jù)格式容忍度問(wèn)題有一段時(shí)間我同步MySQL的數(shù)據(jù)到HDFS時(shí)老報(bào)錯(cuò)日志提示java.lang.NumberFormatException: For input string: abc排查發(fā)現(xiàn)源表某個(gè)int字段里混入了臟數(shù)據(jù)出現(xiàn)了字符串類型的值。DataX默認(rèn)遇到類型轉(zhuǎn)換失敗就會(huì)報(bào)錯(cuò)除非在setting里配置errorLimit。errorLimit有record和percentage兩個(gè)字段含義分別是可以容忍的錯(cuò)誤條數(shù)和錯(cuò)誤比例。生產(chǎn)環(huán)境我一般不設(shè)容忍度因?yàn)榕K數(shù)據(jù)意味著數(shù)據(jù)質(zhì)量有問(wèn)題寧可同步失敗暴露出來(lái)也不要靜默丟掉。但在做一次性數(shù)據(jù)遷移時(shí)我會(huì)把errorLimit的percentage設(shè)為5先保證任務(wù)能跑完拿到絕大部分?jǐn)?shù)據(jù)再回頭單獨(dú)清洗臟數(shù)據(jù)。5.3 HDFS權(quán)限與文件格式坑HDFS寫入報(bào)錯(cuò)最常見(jiàn)的原因是權(quán)限不足。DataX進(jìn)程以什么系統(tǒng)用戶啟動(dòng)HDFS上對(duì)應(yīng)的就是什么身份。如果目標(biāo)目錄的owner和權(quán)限沒(méi)有針對(duì)這個(gè)用戶放通就會(huì)遇到Permission denied。解決辦法有幾個(gè)給目標(biāo)目錄設(shè)置遞歸權(quán)限hdfs dfs -chmod -R 777 /warehouse/ods或者把DataX進(jìn)程的啟動(dòng)用戶切換到HDFS的超級(jí)用戶或者用Kerberos認(rèn)證企業(yè)集群場(chǎng)景我在測(cè)試環(huán)境一般用第一種生產(chǎn)環(huán)境會(huì)專門創(chuàng)建一個(gè)數(shù)據(jù)同步賬戶配置好它的讀寫權(quán)限避免用hdfs超級(jí)用戶跑所有任務(wù)。fileType這個(gè)參數(shù)也經(jīng)常出錯(cuò)。hdfswriter支持text和orc兩種格式。text格式就是普通文本文件調(diào)試方便任何工具都能讀。orc格式是列式存儲(chǔ)壓縮率高、查詢性能好適合數(shù)據(jù)量很大的場(chǎng)景。但如果你的下游是Spark或Presto建議直接用orc省掉后續(xù)轉(zhuǎn)換的麻煩。不過(guò)要注意orc格式對(duì)字段類型要求更嚴(yán)格string和varchar不要混用否則會(huì)出現(xiàn)讀出來(lái)類型不匹配的問(wèn)題。5.4 同步速度遠(yuǎn)低于預(yù)期這個(gè)是群里被問(wèn)得最多的一個(gè)問(wèn)題。明明channel配了8結(jié)果速度只有幾十KB/s。我總結(jié)下來(lái)主要因素不外乎這幾個(gè)splitPk沒(méi)設(shè)置導(dǎo)致并行讀根本沒(méi)生效。源庫(kù)是主從架構(gòu)同步賬號(hào)連接到了只讀節(jié)點(diǎn)只讀節(jié)點(diǎn)的負(fù)載太高查詢性能大幅下降。目標(biāo)端HDFS的DataNode磁盤IO打滿了。JDBC連接串里少了rewriteBatchedStatementstrue導(dǎo)致批量寫入沒(méi)有生效。這個(gè)參數(shù)對(duì)MySQL寫入性能影響巨大加上它之后寫入速度能提升好幾倍。實(shí)測(cè)過(guò)一組對(duì)比同樣的表同樣的channel數(shù)加上rewriteBatchedStatementstrue之后寫入速度從3萬(wàn)條/秒提升到12萬(wàn)條/秒。這個(gè)參數(shù)對(duì)于MySQL寫入場(chǎng)景幾乎是必選項(xiàng)。提示MySQL連接串中建議同時(shí)使用rewriteBatchedStatementstrue和useCompressiontrue前者優(yōu)化批量寫入后者在數(shù)據(jù)量大的時(shí)候減少網(wǎng)絡(luò)傳輸開(kāi)銷。5.5 多表同步時(shí)的類型映射不同數(shù)據(jù)源之間的類型映射是另一個(gè)大坑。DataX有一套自己的類型體系比如long、double、string、date、bytes、boolean它在讀取階段會(huì)把源端類型轉(zhuǎn)換成自己的類型在寫入階段再把內(nèi)部類型轉(zhuǎn)換成目標(biāo)端類型。問(wèn)題是這種轉(zhuǎn)換規(guī)則在不同插件里并不完全一致。比如MySQL的datetime類型在mysqlreader里會(huì)轉(zhuǎn)成date但hdfswriter的date類型和string類型的處理方式不同。如果不小心同步過(guò)去之后時(shí)間字段會(huì)變成一串?dāng)?shù)字或者格式不對(duì)。我的避坑方法是在writer的column里顯式指定目標(biāo)端類型不要依賴自動(dòng)推斷。尤其是時(shí)間類型目標(biāo)端要指定成string并在讀端就做好格式轉(zhuǎn)換。可以在SQL里直接寫date_format(create_time, %Y-%m-%d %H:%i:%s) as create_time這樣源頭輸出就是標(biāo)準(zhǔn)格式字符串寫端直接按string處理完全避開(kāi)類型轉(zhuǎn)換的坑。5.6 常見(jiàn)錯(cuò)誤速查表現(xiàn)象可能原因解決思路任務(wù)啟動(dòng)即報(bào)ClassNotFoundlib目錄缺插件jar包重新完整解壓DataX檢查plugin目錄讀取速度極慢未設(shè)置splitPk按主鍵設(shè)置splitPk寫入速度極慢缺少rewriteBatchedStatementsMySQL連接串加上該參數(shù)JSON解析報(bào)錯(cuò)字段類型寫錯(cuò)或缺少逗號(hào)用python -m json.tool校驗(yàn)?zāi)繕?biāo)端重復(fù)數(shù)據(jù)writeMode用錯(cuò)更新MySQL寫模式為replaceHDFS路徑不存在未提前建目錄調(diào)度流程里加mkdir步驟內(nèi)存溢出channel數(shù)量過(guò)多或byte限制過(guò)大降低channel設(shè)置byte流控6. 一些心得體會(huì)用了DataX這么久最大的體會(huì)是它的定位很精準(zhǔn)它不搞服務(wù)化不做Web界面不負(fù)責(zé)調(diào)度只專注做一件事——把數(shù)據(jù)穩(wěn)定地從A搬到B。這種“單點(diǎn)做好”的思路反而讓我在排障時(shí)特別舒服因?yàn)槊總€(gè)環(huán)節(jié)都單一、透明出問(wèn)題就能快速定位。我現(xiàn)在的做法是把所有同步任務(wù)按業(yè)務(wù)線組織成獨(dú)立目錄每個(gè)任務(wù)一個(gè)JSON。在調(diào)度平臺(tái)里按天觸發(fā)跑完自動(dòng)記錄日志。長(zhǎng)期運(yùn)行下來(lái)非常穩(wěn)定基本不需要人為干預(yù)。DataX的另一個(gè)好用之處是它的插件機(jī)制。如果現(xiàn)有插件滿足不了需求你甚至可以自己寫一個(gè)插件只要繼承它提供的接口打包放進(jìn)plugin目錄就能被識(shí)別。有一次我需要從一個(gè)內(nèi)部自研的存儲(chǔ)系統(tǒng)同步數(shù)據(jù)就是照著官方插件的代碼結(jié)構(gòu)自己寫了個(gè)reader整個(gè)過(guò)程大概花了一天時(shí)間這種自由度是很多商業(yè)同步工具給不了的。最后分享一個(gè)小技巧DataX在跑任務(wù)的時(shí)候日志里會(huì)打印每個(gè)channel的實(shí)時(shí)流量如果發(fā)現(xiàn)某個(gè)channel長(zhǎng)期沒(méi)有輸出大概率是數(shù)據(jù)傾斜了。這時(shí)候可以用splitPk配合一個(gè)更均勻的字段比如把字符串主鍵換成自增主鍵或者增加多個(gè)字段拼接成切分鍵基本能解決。如果你是剛接觸DataX建議先從最簡(jiǎn)單的MySQL到MySQL開(kāi)始跑通了再往上疊加HDFS、Hive這些復(fù)雜目標(biāo)端。每一步都確認(rèn)數(shù)據(jù)一致之后再擴(kuò)展場(chǎng)景就不會(huì)在踩坑中浪費(fèi)時(shí)間。