從一次數(shù)據(jù)遷移項(xiàng)目里,我得到的四個(gè)經(jīng)驗(yàn)教訓(xùn)
不久前,我經(jīng)歷了一次數(shù)據(jù)遷移項(xiàng)目。前幾天,我跟一位架構(gòu)師探討了一下當(dāng)時(shí)的各個(gè)步驟,和我所選擇并進(jìn)一步開發(fā)的解決方案。我覺(jué)得我應(yīng)該告訴他一些信息 ,避免他日后遷移數(shù)據(jù)時(shí)踩坑。
在我們的交流中 ,我提到了數(shù)據(jù)遷移的各種難題和我們遇到的問(wèn)題。現(xiàn)在我意識(shí)到,這些東西對(duì)許多從事數(shù)據(jù)遷移項(xiàng)目的人們來(lái)說(shuō)都很有用。我聽說(shuō)這些是很常見的問(wèn)題,多是那些開始數(shù)字化轉(zhuǎn)型的公司容易遇到的 。數(shù)據(jù)遷移項(xiàng)目通常是一套解決方案,讓你提取、轉(zhuǎn)換舊數(shù)據(jù),然后將其存儲(chǔ)到新的系統(tǒng)中。
之前沒(méi)想到的是,我從事軟件工作以來(lái)只參與過(guò)一個(gè)數(shù)據(jù)遷移項(xiàng)目。感覺(jué)好像回到了我在學(xué)習(xí) SQL 時(shí)掙扎的日子。那時(shí)的經(jīng)歷很有意思,你在 Oracle 和 MariaDB 上都使用 PL/ SQL ,并為此頭痛不已。你只能自行猜測(cè)哪個(gè)是舊系統(tǒng),哪個(gè)是光芒萬(wàn)丈的新系統(tǒng)。但今天不講這個(gè),今天講我認(rèn)為導(dǎo)致延遲交付的最大陷阱。觀點(diǎn)是我自己的,但事情卻是大家都會(huì)遇到的,等等等等。
1. 用 SQL 腳本做主要工具
這是昨天早上我忘了向同事強(qiáng)調(diào)的一個(gè)問(wèn)題,今天早上它又在我腦海閃現(xiàn)。別誤會(huì),SQL 是強(qiáng)大的 數(shù)據(jù) 檢索和顯示工具。但是,當(dāng)你有一個(gè)由多個(gè)開發(fā)人員組成的團(tuán)隊(duì),并在同一個(gè)代碼庫(kù)上工作時(shí),關(guān)鍵要確保你的更改能與其他代碼很好地整合。
問(wèn)題在于,要驗(yàn)證不同的場(chǎng)景 時(shí) ,我們不能只花幾秒鐘或幾分鐘運(yùn)行典型的單元測(cè)試。我們必須執(zhí)行實(shí)際的遷移,因此我不會(huì)稱之為“集成測(cè)試”,因?yàn)?集成測(cè)試的 環(huán)境與實(shí)際環(huán)境有所不同(后文會(huì)詳細(xì)介紹)。
我們必須啟動(dòng) docker 鏡像,然后給將來(lái)要 用真實(shí)數(shù)據(jù)應(yīng)對(duì) 的每種 場(chǎng)景 加載虛擬數(shù)據(jù)。我覺(jué)得我們的 Jenkins 構(gòu)建一次要 2-3 個(gè)小時(shí)才能完成。這使本地開發(fā)更加困難,因?yàn)闆](méi)有人愿意花 5 分鐘內(nèi)改代碼然后花 2-3 個(gè)小時(shí)來(lái)測(cè)試。最開始我們改為只運(yùn)行我們需要的那些測(cè)試用例。那時(shí) CI 慢 到, 我甚至在上一年專門發(fā)了一個(gè)帖子講這個(gè)事情 。最終,我們將時(shí)間降到了 40 分鐘,仍然很慢,但考慮到我們正在處理的內(nèi)容,可能這就是我們最好的選擇了。
https://www.codingnagger.com/2019/01/31/slow-ci/
現(xiàn)在我不是 在談我自己的經(jīng)驗(yàn) ,而是 在說(shuō) 一些跟我討論過(guò)的架構(gòu)師 的看法 ,他們?cè)谀莻€(gè)項(xiàng)目 期間,甚至項(xiàng)目結(jié)束之后都向我建議 ,用一種實(shí)際的編程語(yǔ)言可以使我們免于這種痛苦:你可以 測(cè)試任意組件,完整的檢索 、轉(zhuǎn)換和加載 操作 只需幾秒鐘就夠了。然后你再對(duì)理想路徑進(jìn)行一次集成測(cè)試。我們本來(lái)可以把 CI 構(gòu)建控制在一分鐘之內(nèi)的,從而節(jié)省很多時(shí)間。
2. 源字段和目標(biāo)字段對(duì)不上
字段不匹配是很痛的痛點(diǎn)。我不是指從源數(shù)據(jù)字段到目標(biāo)數(shù)據(jù)字段的對(duì)應(yīng)錯(cuò)誤,而是指字段對(duì)應(yīng)沒(méi)問(wèn)題,但目標(biāo)字段類型不對(duì)。由于數(shù)據(jù) 的 敏感 性 ,我們研究解決方案時(shí)接觸不到真實(shí)數(shù)據(jù)。
所以這種問(wèn)題只有到了在生產(chǎn)環(huán)境運(yùn)行時(shí)才會(huì)暴露出來(lái)。你可能會(huì)有一些源字段是字符串類型的,但目標(biāo)字段卻是整型的。當(dāng)所有測(cè)試數(shù)據(jù)都是數(shù)值時(shí)不會(huì)有問(wèn)題,但當(dāng)在幾百萬(wàn)實(shí)體中出現(xiàn)哪怕一兩條包含字母時(shí),就全都完了。還有些時(shí)候數(shù)據(jù)會(huì)被截?cái)啵驗(yàn)槟繕?biāo)字段所能表達(dá)的值范圍比源字段要小。這種問(wèn)題不是數(shù)據(jù)遷移工程的責(zé)任,因?yàn)槟繕?biāo)系統(tǒng)不是我們?cè)O(shè)計(jì)的,但實(shí)際上我們?cè)诮桓稊?shù)據(jù)遷移方案時(shí)卻不得不去修復(fù)這種問(wèn)題。是的,現(xiàn)實(shí)并沒(méi)有那么理想。
所以在這里我要強(qiáng)調(diào)的是,如果你要構(gòu)建一個(gè)系統(tǒng)的新版本,請(qǐng)確保新的數(shù)據(jù)庫(kù)字段的類型和格式都能匹配源數(shù)據(jù)。我們不能截?cái)嗟刂坊螂娫捥?hào)碼,尤其是當(dāng)我們系統(tǒng)需要這些信息時(shí)。
3. 與其他團(tuán)隊(duì)邊界不明確
當(dāng)時(shí),我的團(tuán)隊(duì)是做數(shù)據(jù)遷移的。我們?cè)O(shè)計(jì)了一個(gè)解決方案,把數(shù)據(jù)從這里遷移到那里。但如上文所述,我們有時(shí)不得不修復(fù)目標(biāo)數(shù)據(jù)庫(kù)的問(wèn)題,這些問(wèn)題都是其他團(tuán)隊(duì)為各種功能折騰出來(lái)的。最重要的是,我不明白我的團(tuán)隊(duì)怎么就變成了其他團(tuán)隊(duì)的測(cè)試數(shù)據(jù)提供者。反正 這些團(tuán)隊(duì)不會(huì)把所有的測(cè)試數(shù)據(jù)匯總在一起以便測(cè)試其功能,而是會(huì)來(lái)找我們?yōu)樗麄兩呻S機(jī)的測(cè)試數(shù)據(jù)。
回想起來(lái),這么做真蠢。因此,我們構(gòu)建的測(cè)試框架中有一個(gè)類用于生成數(shù)據(jù)。在 開發(fā)時(shí),我們把這些數(shù)據(jù)存到源數(shù)據(jù)庫(kù)里,然后運(yùn)行遷移過(guò)程,提取、轉(zhuǎn)換這些數(shù)據(jù),并把它們存到目標(biāo)數(shù)據(jù)庫(kù)里。接著再?gòu)哪繕?biāo)系統(tǒng)中導(dǎo)出這些數(shù)據(jù)發(fā)送給那些團(tuán)隊(duì)。我們不得不這么做,因?yàn)槲覀儾幌朐谖覀兊穆氊?zé)范圍之外制造數(shù)據(jù)。但是,我認(rèn)為我們做的太多了。我們應(yīng)該把 底線控制 在“請(qǐng)您自行創(chuàng)建測(cè)試數(shù)據(jù)”上。
雖然幫助他人 也是可以的 ,但我們不能在自己本職工作都沒(méi)做完的情況下這么做。最后的結(jié)果就是,我們負(fù)責(zé)了整個(gè)工程的三個(gè)主要部分:數(shù)據(jù)遷移、修復(fù)目標(biāo)數(shù)據(jù)庫(kù)的問(wèn)題、給每個(gè)人生成測(cè)試數(shù)據(jù)。
4. 不同環(huán)境的設(shè)置
我記得當(dāng)時(shí)我沒(méi)有過(guò)多考慮 各種部署 環(huán)境的不同設(shè)置。從開發(fā)環(huán)境到預(yù)發(fā)布環(huán)境,再到生產(chǎn)環(huán)境,它們 會(huì) 有很多差異。顯然,我們?yōu)榇烁冻隽舜鷥r(jià)。你可能會(huì)認(rèn)為不同版本間的 Oracle 數(shù)據(jù)庫(kù)或 MariaDB 數(shù)據(jù)庫(kù)應(yīng)該不會(huì)有什么大問(wèn)題吧?但如果我告訴你下個(gè)版本跟這個(gè)版本的差異會(huì)破壞掉你所有的 SQL 腳本呢?就像必須把 VALUES 替換成 VALUE。
想象一下遷移工具在你本地運(yùn)行得好好的,接下來(lái)你把它推送到一個(gè)緩慢的 CI 流程 。然后你再把它發(fā)布到一個(gè)環(huán)境,運(yùn)行遷移過(guò)程并檢查 ,沒(méi)什么問(wèn)題 。結(jié)果到生產(chǎn)環(huán)境出問(wèn)題了,因?yàn)樯a(chǎn)環(huán)境的 MariaDB 版本太老。此外,生產(chǎn)環(huán)境還是個(gè) EC2 實(shí)例,而預(yù)發(fā)布環(huán)境則是 RDS。
這個(gè)項(xiàng)目在開發(fā)環(huán)境和在生產(chǎn)環(huán)境的變量設(shè)置完全一致,但我還是被它們輸出的差異驚到了。為 了 在不同集成環(huán)境里都能工作而到處改代碼 ,那些痛苦你都逃開了 。生產(chǎn)環(huán)境的配置 本應(yīng)能 證明你的解決方案可以在生產(chǎn)環(huán)境工作,但 其實(shí) 它跟真實(shí)的生產(chǎn)環(huán)境配置一點(diǎn)也不像, 這肯定就會(huì)出問(wèn)題 。這絕對(duì)是我在這次經(jīng)歷中得到的最大一筆經(jīng)驗(yàn)。
5. 總結(jié)
我將在余生中繼續(xù)學(xué)習(xí)從舊項(xiàng)目獲得的經(jīng)驗(yàn)教訓(xùn)。我甚至?xí)販剡@篇博客文章來(lái)確保我不會(huì)忘記這些經(jīng)驗(yàn)教訓(xùn),因?yàn)樗鼈冊(cè)谖蚁麓芜M(jìn)行數(shù)據(jù)遷移時(shí)還是非常有用的。更妙的是,其中一些經(jīng)驗(yàn)教訓(xùn)不僅僅可以用于數(shù)據(jù)遷移,還可以應(yīng)用于其他方面。
即使這次我沒(méi)有去找個(gè)工具來(lái)做, 本文談到 的 這些 經(jīng)驗(yàn)也讓我堅(jiān)信應(yīng)該找個(gè)好工具來(lái)做好工作。信任已有信息固然很好,但也不妨 去看看周圍 ,對(duì)自己也沒(méi)有什么壞處。有時(shí)這些工具并不比 SQL 查詢慢。
其次,盡可能確保開發(fā)環(huán)境的設(shè)置與生產(chǎn)環(huán)境匹配。這將避免許多集成問(wèn)題。
最后,當(dāng)職責(zé)明確了之后,應(yīng)避免給自己攬更多的活兒,它們會(huì)妨礙你的本職工作。塞爾吉奧·拉莫斯(Sergio Ramos)并不是世界上最好的后衛(wèi),因?yàn)樗举惣镜梅殖^(guò)菲爾米諾。最好的后衛(wèi)應(yīng)該是先做好自己擅長(zhǎng)的防守工作,然后偶爾進(jìn)進(jìn)球。