數(shù)據(jù)庫(kù)數(shù)據(jù)同步,結(jié)果踩了 7 個(gè)大坑!
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
你是小阿巴,剛?cè)肼氁患译娚坦尽?/span> ![]() 第一天上班,老板就交給你一個(gè)艱巨的任務(wù):定期把公司的訂單數(shù)據(jù)同步到數(shù)據(jù)分析倉(cāng)庫(kù)。 一聽(tīng)到數(shù)據(jù)同步這 4 個(gè)字,你立刻汗流浹背了。 ![]() 你的哥哥程序員魚皮,曾經(jīng)就是在大公司負(fù)責(zé)數(shù)據(jù)同步。結(jié)果雙十一當(dāng)天,近 2 小時(shí)的訂單數(shù)據(jù)沒(méi)有同步過(guò)去。數(shù)據(jù)分析團(tuán)隊(duì)看到的數(shù)據(jù)是 2 小時(shí)前的,以為銷量沒(méi)達(dá)到預(yù)期,就沒(méi)有及時(shí)給熱銷商品補(bǔ)貨。最終錯(cuò)失了 1 個(gè)多億的銷售額!魚皮也因此被老板優(yōu)化掉了。 ![]() 作為一名程序員,怎能輕易退縮?你握緊拳頭,一定要完成好這個(gè)任務(wù)! ![]()
全量同步什么是數(shù)據(jù)同步呢?就像你有兩個(gè)手機(jī),要把其中一個(gè)手機(jī)的照片復(fù)制到另一個(gè)手機(jī)里。數(shù)據(jù)同步就是把一個(gè)數(shù)據(jù)庫(kù)的數(shù)據(jù),定期復(fù)制到另一個(gè)數(shù)據(jù)庫(kù)里。 你心想:這還不簡(jiǎn)單嗎?我寫個(gè)定時(shí)任務(wù),每天 把整個(gè)訂單表的數(shù)據(jù) 全部查出來(lái),然后 一股腦插入 到數(shù)據(jù)倉(cāng)庫(kù)! ![]() 這種方法叫 全量同步,不管數(shù)據(jù)有沒(méi)有變化,每次都把所有數(shù)據(jù)重新復(fù)制一遍,簡(jiǎn)單粗暴。
第一天,公司有 1 萬(wàn)條訂單數(shù)據(jù)。你的程序跑了 3 個(gè)小時(shí),同步成功! 老板看著數(shù)據(jù)夸到:不錯(cuò)啊小阿巴,開(kāi)發(fā)神速??! ![]() 你內(nèi)心暗爽:高端的功能往往只需要最簡(jiǎn)單的代碼。 ![]() 基礎(chǔ)告警第二天一早,你還沒(méi)到公司,就收到了運(yùn)營(yíng)小姐姐的奪命連環(huán) call:“小阿巴!出大事了!昨天晚上的數(shù)據(jù)同步失敗了,現(xiàn)在老板還沒(méi)看到昨天的數(shù)據(jù),他正在會(huì)議室發(fā)火呢!” ![]() 你意識(shí)到:現(xiàn)在的程序是不可靠的,如果因?yàn)榫W(wǎng)絡(luò)等原因同步失敗了,可能要到第二天才會(huì)發(fā)現(xiàn)。 于是你給程序加了一段邏輯:如果同步失敗,會(huì)記錄錯(cuò)誤日志,同時(shí)發(fā)郵件通知管理員,并且把數(shù)據(jù)庫(kù)回滾到同步前的狀態(tài)。 ![]() 這樣如果出了問(wèn)題,你會(huì)比老板先發(fā)現(xiàn),手動(dòng)重新執(zhí)行一遍同步任務(wù)就好。 增量同步過(guò)了幾天,老板黑著臉找你了:“小阿巴,為什么今天數(shù)據(jù)還沒(méi)同步完?” 你看了下訂單數(shù)據(jù),立刻發(fā)現(xiàn)了問(wèn)題。現(xiàn)在有 10 萬(wàn)條數(shù)據(jù),程序跑了 30 個(gè)小時(shí)還沒(méi)結(jié)束;這樣下去,如果有 100 萬(wàn)條數(shù)據(jù),估計(jì)要 300 個(gè)小時(shí)! 老板:少?gòu)U話,快點(diǎn)解決,不然送你到隔壁餐飲部沉淀沉淀。 ![]() 你開(kāi)始思考:既然全量同步太慢,那我只同步每天新增的訂單不就行了? ![]() 這就是 增量同步,約定每天 0 點(diǎn)執(zhí)行同步任務(wù),只同步昨天 0 點(diǎn)之后創(chuàng)建的訂單。 ![]() 這樣大大減少了每次同步的數(shù)據(jù)量,任務(wù)的執(zhí)行時(shí)間不再線性增加了。 但很快,你發(fā)了一個(gè)問(wèn)題,訂單的狀態(tài)是會(huì)發(fā)生變化的,比如用戶付款后又退款。 如果使用訂單創(chuàng)建時(shí)間作為增量同步的分界標(biāo)志,只能同步新增的訂單,但是訂單狀態(tài)的更新不會(huì)同步! ![]() 于是,聰明的你使用 updated_time 字段進(jìn)行增量同步,這個(gè)字段會(huì)在每次數(shù)據(jù)變化時(shí)自動(dòng)更新。 ![]() 這樣新增和修改的數(shù)據(jù)都能同步了! 你內(nèi)心竊喜:聰明如我小阿巴,老板夸我好開(kāi)發(fā)。 ![]() 批處理過(guò)了幾天,公司搞了一波大促活動(dòng),當(dāng)天訂單量比平時(shí)多了幾倍,老板請(qǐng)大家通宵狂歡開(kāi) party。 結(jié)果正在你歡唱 “只因你太美” 的時(shí)候,突然公司的運(yùn)維大叫:不好了不好了,我們的項(xiàng)目服務(wù)器卡死了,用戶無(wú)法下單! 你看了看時(shí)間,0 點(diǎn)多,不正是數(shù)據(jù)同步任務(wù)的執(zhí)行時(shí)間么?你瞬間汗流浹背,放下麥克風(fēng),趕回公司修 Bug。 原來(lái)是因?yàn)橐淮尾樵兂鰜?lái)的訂單數(shù)據(jù)太多了,都加載到內(nèi)存,導(dǎo)致服務(wù)器 OOM 內(nèi)存溢出了。 ![]() 你悔不當(dāng)初:唉,早該想到這個(gè)問(wèn)題,既然單次處理數(shù)據(jù)量太大有風(fēng)險(xiǎn),那我就 分批處理。 每 100 條數(shù)據(jù)為一批,每次只從數(shù)據(jù)庫(kù)中分頁(yè)查詢出這一批數(shù)據(jù),同步完這一批,再執(zhí)行下一批。 ![]() 這樣每次只處理少量數(shù)據(jù),內(nèi)存壓力?。欢胰绻骋慌幚硎?,只需要回滾和重新同步這一批,而不是全部重來(lái)。 問(wèn)題算是解決了,但是今夜你徹夜難眠,你似乎成了公司業(yè)務(wù)增長(zhǎng)后,唯一不開(kāi)心的那個(gè)人。 ![]() 希望今后不會(huì)再遇到這種鬧心事了吧。 游標(biāo)機(jī)制但生活總是這樣,屋漏偏逢連夜雨。兩天后,你又收到了老板的咆哮:“狗阿巴,這就是你做的數(shù)據(jù)同步?你自己看看丟了多少數(shù)據(jù)!” 你大驚,丟失數(shù)據(jù)?不應(yīng)該啊。。。 經(jīng)過(guò)仔細(xì)排查,你發(fā)現(xiàn)了問(wèn)題:如果在分頁(yè)查詢的過(guò)程中,有新的數(shù)據(jù)被插入或更新,就會(huì)導(dǎo)致數(shù)據(jù)偏移和丟失! 比如查詢第 1 頁(yè)時(shí),符合條件的訂單有 4 條:
執(zhí)行第 1 頁(yè)查詢,每頁(yè) 2 條,偏移量為 0:
查詢結(jié)果返回訂單 A(08:00)和訂單 B(09:00),同步完成后將偏移量調(diào)整為下一頁(yè) 可就在查詢下一頁(yè)前,訂單 B 因?yàn)?“修改訂單狀態(tài)” 被更新,這時(shí)訂單 B 的更新時(shí)間就不在查詢范圍內(nèi)了。 這就導(dǎo)致查詢下一頁(yè) ![]() 怎么解決這個(gè)問(wèn)題呢? 這可難不倒你小阿巴,既然 動(dòng)態(tài)數(shù)據(jù)集 中使用 SQL 自帶的 OFFSET 偏移作為分頁(yè)起點(diǎn)會(huì)出問(wèn)題,那不妨 自定義一個(gè)標(biāo)志來(lái)記錄下一批要同步的起點(diǎn)。 這就是游標(biāo)機(jī)制。 比如我約定自增的主鍵 id 作為游標(biāo),每查詢一批數(shù)據(jù)之后,把這批數(shù)據(jù)的最后一條記錄作為新的游標(biāo)值;查詢下一批數(shù)據(jù)時(shí),只查詢 id > 游標(biāo)的數(shù)據(jù)。 ![]() 這樣不僅防止數(shù)據(jù)丟失,還避免了 OFFSET 深度分頁(yè)帶來(lái)的性能問(wèn)題。 此外,如果把游標(biāo)想象成進(jìn)度條的斷點(diǎn),可以更清晰地記錄同步進(jìn)度,失敗后可以從斷點(diǎn)繼續(xù)。 做完這一通優(yōu)化后,你長(zhǎng)吁了一口氣,工作看來(lái)是保住了。 ![]() 性能優(yōu)化你以為終于可以安穩(wěn)一段時(shí)間了。但沒(méi)想到,公司的好日子才剛剛開(kāi)始。 隨著老板大力擴(kuò)張業(yè)務(wù),公司每天的訂單量可以達(dá)到百萬(wàn)條,你的同步任務(wù)每次要跑幾個(gè)小時(shí)才能完成。 更要命的是,老板現(xiàn)在對(duì)數(shù)據(jù)的依賴越來(lái)越強(qiáng),要求每隔 2 個(gè)小時(shí)就要同步一次數(shù)據(jù)! 怎么能夠讓任務(wù)執(zhí)行更快呢? 這時(shí),你想起了曾經(jīng)在 程序員面試神器 - 面試?guó)?/a> 上背過(guò)的各種性能優(yōu)化八股文。好家伙,終于能派上用場(chǎng)了! ![]() 首先,修改插入數(shù)據(jù)庫(kù)的操作方式。之前是一條一條地插入訂單數(shù)據(jù),改為執(zhí)行一條批處理語(yǔ)句來(lái)同時(shí)插入同一批內(nèi)多個(gè)訂單數(shù)據(jù),減少了跟數(shù)據(jù)庫(kù)通信的次數(shù),性能大幅提升。 ![]() 但是你覺(jué)得還不夠快,于是優(yōu)化了批處理的流程。之前是等一批同步完再同步下一批,串行執(zhí)行;現(xiàn)在你啟動(dòng)了多個(gè)線程,每個(gè)線程負(fù)責(zé)處理一批訂單的同步,多個(gè)線程可以同時(shí)搬磚干活,效率大幅提高。 ![]() 這一通操作下來(lái),老板都激動(dòng)了:“小阿巴,你這技術(shù)可以??!好好干,我給你升職加薪!” 你笑了,有了老板這句話,你想繼續(xù)努力給公司賣命了。 ![]() 實(shí)時(shí)同步兩年后,隨著你頭發(fā)的消逝,公司總部決定要上市了! ![]() 老板找到了你:“阿巴阿巴,咱們的數(shù)據(jù)同步能不能做到實(shí)時(shí)?我想給投資人展示實(shí)時(shí)的業(yè)務(wù)監(jiān)控。最好是用戶剛下單,投資人立刻就能在大屏幕上看到!" ![]() 你早料到會(huì)有這個(gè)需求,帥氣地甩出一句話:“交給我吧,我讓你見(jiàn)識(shí)一下企業(yè)級(jí)方案!” ![]() 這兩年你也沒(méi)閑著,像實(shí)時(shí)數(shù)據(jù)分析這種典型需求的實(shí)現(xiàn)方案,你早已爛熟于心。 普通的定時(shí)任務(wù)已經(jīng)無(wú)法滿足實(shí)時(shí)性的要求,只能搬出 CDC + 消息隊(duì)列 這兩大殺器了。 CDC(Change Data Capture)就像給數(shù)據(jù)庫(kù)安裝了一個(gè) 24 小時(shí)實(shí)時(shí)監(jiān)控的攝像頭,數(shù)據(jù)有任何變化,攝像頭都能立刻發(fā)現(xiàn)。 消息隊(duì)列就像一個(gè)快遞中轉(zhuǎn)站。數(shù)據(jù)庫(kù)變化的消息先發(fā)送到這個(gè)中轉(zhuǎn)站,然后同步數(shù)據(jù)的程序從中轉(zhuǎn)站取出數(shù)據(jù)并寫入到數(shù)據(jù)分析倉(cāng)庫(kù)中。 ![]() 有了中轉(zhuǎn)站后,如果消息特別多,程序來(lái)不及處理,也不會(huì)丟失數(shù)據(jù)。 實(shí)現(xiàn)了這套方案之后,用戶只要一下單,100 毫秒內(nèi)老板就能在儀表盤上看到。而且保險(xiǎn)起見(jiàn),你還給消息隊(duì)列加了個(gè)監(jiān)控,如果消息堆積太多,就會(huì)自動(dòng)告警。 他開(kāi)心得像個(gè)孩子:“小阿巴,我給你升職加薪,還給你帶新人!” 你又笑了,開(kāi)始幻想著你和新人坐在高高的辦公桌上,你給他講述自己光輝事跡,他向你投來(lái)羨慕的目光。 ![]() 方案完善半個(gè)月后,公司迎來(lái)了雙十一大促活動(dòng)。 你正在和新來(lái)的實(shí)習(xí)生阿坤吹牛皮,沒(méi)想到瞬間產(chǎn)生了大量訂單,像洪水一般,讓你的實(shí)時(shí)同步系統(tǒng)出現(xiàn)了各種異常:
![]() 監(jiān)控大屏上各種告警此起彼伏,老板的臉色越來(lái)越難看:“狗阿巴,這就是你說(shuō)的企業(yè)級(jí)方案?” 這一刻,你有點(diǎn)恍惚:“我只是知道可以這么實(shí)現(xiàn),但沒(méi)人告訴我出了這些問(wèn)題怎么解決。。。” 這時(shí),你身旁的實(shí)習(xí)生阿坤說(shuō)話了:“我來(lái)!” ![]() 只見(jiàn)他對(duì)著電腦一通操作,嘴里振振有詞: 1)消息重復(fù)問(wèn)題,可以通過(guò)冪等機(jī)制解決。給每條消息一個(gè)唯一編號(hào),處理過(guò)的消息編號(hào)記錄下來(lái),就不會(huì)重復(fù)處理了。 ![]() 2)消息亂序問(wèn)題,可以通過(guò)分區(qū)解決。把相關(guān)的消息放到同一個(gè)分區(qū),保證同一個(gè)訂單的消息按順序處理。 ![]() 3)消息堆積問(wèn)題,可以通過(guò)搭建集群和動(dòng)態(tài)擴(kuò)容,增加對(duì)消息的處理能力。 ![]() “這些點(diǎn)在使用消息隊(duì)列來(lái)實(shí)現(xiàn)數(shù)據(jù)同步的時(shí)候就要考慮到才對(duì)呀!” 你聽(tīng)著阿坤說(shuō)的話,默默低下了頭,內(nèi)心五味雜陳,這就是應(yīng)屆生的水平么? 阿坤看著你的代碼接著說(shuō):“不是哥,做個(gè)數(shù)據(jù)同步要這么麻煩么?DataX、Canal、Debezium,這么多現(xiàn)成的企業(yè)級(jí)數(shù)據(jù)同步工具你不用?非要自己寫代碼?” ![]() 你無(wú)言以對(duì),內(nèi)心感受到了來(lái)自實(shí)習(xí)生的一噸暴擊。 阿坤甚至一臉嫌棄地看著你:“不是哥們,連數(shù)據(jù)對(duì)賬都不做么?你自己都不定期檢查同步前后的數(shù)據(jù)是否一致么?我這有份數(shù)據(jù)同步的企業(yè)級(jí)方案,你好好看看吧?!?/span> ![]() ![]() 老板拍了拍阿坤的肩膀:“小伙子優(yōu)秀,導(dǎo)師的導(dǎo)師,今后公司的未來(lái)就靠你了,我給你升職加薪。至于老阿巴,你好自為之吧?!?/span> 這個(gè)城市,又多了一個(gè)傷心的人。 ? 閱讀原文:原文鏈接 該文章在 2025/10/11 10:14:44 編輯過(guò) |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |