概要
本次分享將從一次實際的數據庫割接案例出發,通過介紹系統遷移過程中的項目背景、數據同步方案、工具開發、仿真測試、割接心理素質等方面展開,管窺工程實踐過程中鮮為人知的幕后故事。
項目背景
某企業支撐系統,已經連續服務七年有余。算起來比我的工作年限還要長。
歷年工程中,硬件、軟件、運營團隊都更新換了好幾茬,單獨系統核心數據庫—— 一臺小型機搭載Oracle 10g,附加一套磁盤陣列,從來沒有動過。隨著近年的業務暴漲、負載上升、硬件老化,服務器、磁盤都時有故障發生,負載水平線逼近極限,故障率還有加速抬頭的趨勢。整個運營團隊面臨了巨大的客戶壓力,提升系統穩定性的巨大挑戰擺在了大家面前。
數據:CPU平均空閑率26.7% ,隔三差五就有空閑率逼近0%的異常告警單,繁忙的磁盤IO。
主要困難
困難1: 團隊大動蕩
我是在“天塌地陷”的不利局面中加入到項目組。原運營團隊,包括但不限于資深項目經理、技術負責人、多名工程師等,先后因各種原因,在很短的時間內集中離職了。在接手之前,我對該項目一無所知,接手以后很短暫的交接過程中,很難獲取多少有價值的信息。
此外,該系統連續突發重大故障,項目組不但要解決問題,還要面臨巨大的心理壓力、商務壓力,整個團隊疲憊不堪、士氣低落。每一次重大故障,所有人都得沒日沒夜地干活,處理好以后還需要寫匯報材料,匯報之后也未必能得到客戶的肯定。甚至在某種程度上說,急劇增長的故障率,進一步刺激、助漲了離職率。正如一位哲人所說:
降低故障率是提升團隊幸福指數的首要保障。— 弗拉基米.耶維奇.嚴
困難2: 拓撲大調整
在技術上,本次遷移的另一個難點是系統拓撲結構的調整。系統的拓撲結構最初是星型:以數據庫和應用服務器為核心,外掛近100臺各類采集服務器。采集服務器的網絡又分為DCN網和公網兩個部分。
星型結構雖然簡單易用,但是安全隱患也非常明顯。在早期建設的時候,規范尚未健全,都是在核心服務器中配置雙網卡,連通內網、外網。在本期工程中,非常明確必須要完成內外網分離改造。
困難3: 安全一票否決
Oracle 部署主版本由10g 升級到 11g,加強管理訪問權限。最大限度地提高安全性,口令60天更換一次,同時要求不能因為更換口令而中斷業務;如果出現連續的錯誤口令訪問,甚至可以不惜鎖定數據庫。
從安全的角度看,類似這些規定可以降低風險。但是在軟件架構規范化不足、自動化嚴重不足的條件下,卻給我們的遷移工作增加了難度。
遷移前準備
基于上述三大問題,在正式遷移之前主要做了下列幾項工作:
- 加強監控手段,降低日常故障率。梳理需要監控的基礎指標和業務指標,側重關鍵業務可用性。例如,某業務的正常調度周期是3小時,部署模擬腳本,將模擬腳本的調度周期提高到5分鐘一次甚至更高,通過高頻率的調用執行,主動觸發風險點,一些隱藏的問題就比較容易暴露出來。
- 重點培訓新人,穩定隊伍。本質上說,這次遷移工作的首要任務不在技術、也不在數據,而在于人。上一個團隊整體流失,新補充的人員又完全沒有相關經驗,可以說是從0開始。基于該階段的特殊情況,我選擇了實質性暫停遷移工作,而把主要的精力投入到人員培訓和組織重建上來。關于這塊內容比較復雜,實際是另外一個主題,計劃后續再發布,敬請關注。
- 梳理全局視圖。全局視圖包括兩個維度:技術和人。
第一,重繪系統架構圖。部分參考現有文檔資料,但是主要立足于自主調研。繪制的過程,即是收集、整理的過程,也是制定遷移方案的思考過程。唯有自己動手,才能加深認識,做到胸有成竹。
第二,重新梳理系統干系人。主要通過大量接觸各方面的領導、配合部門以及第三方廠家。個人工作經歷方面,獨立工作的場景居多,自己能直接控制的情況居多,不太需要理會復雜的部門關系、人際關系。這項工作對于某些人來說比較容易,但是對我而言,其實是有過一段比較困難的過程。
遷移前準備:數據同步
主要實現方案:OGG + DBLINK + 自主定制遷移程序。
Oracle Golden Gate
在最早的方案中,我們打算完全依賴Oracle Golden Gate (以下簡稱OGG)。
但是在實驗階段發現,實施該方案有其限制條件。
首先,歷史遺留系統有龐大的歷史數據,如果都用OGG,無法確保新庫的及時性、一致性。其次、由于管理的不規范,存在很多該做分區而沒有做分區的大表,而且迭代過程中本身會產生數量眾多、非必要的表,一時還真的很難分離出來。
EXP/IMP
連接源庫執行:
exp userid=username/pass
file=/oradata5/ogg/dp/part.dmp
log=/oradata5/ogg/dp/part.log
buffer=819200 feedback=10000 GRANTS=n INDEXES=n COMPRESS=n RECORD=n TRIGGERS=n CONSTRAINTS=n
parfile=/oradata5/ogg/dp/part.par
vi part.par
tables=(
TABLE_A:P_20160531,
TABLE_B:P_20160526,
TABLE_C:P_20160526
)
連接目標庫執行(注意NLS_LANG保持一致):
imp username/pass@dbnms file=part.dmp buffer=819200 log=part_imp.log ignore=y
** DATABASE LINK **
dblink建立之后,連接其中一個庫就可以對兩個庫執行SQL,它的優勢是提供了舊庫->新庫之間的連接通道。也就僅僅是個通道而已。
insert into TableA(….) select … from TableA@linkname where colltime=‘’;
**定制遷移程序 **在某些特殊情況下,無論使用 OGG 和 DBLINK 都會存在一定問題。
例如A表是大量的原始數據,每天一個分區,每個分區約為4000萬條記錄,一個月就有1.2億條。由于業務上非常重要,該表的數據必須遷移到新庫。這種情況下,我們就只能自己編寫遷移腳本,實現數據推送。
堅持“少量、多批次、并行”的原則。首先,控制每個批次提交的記錄數,將每個分區4000萬的數據,切分成10萬一份的小切片,這樣即使失敗也能快速重試,還能杜絕UNDO表空間暴漲(例如exp/imp整個分區的方式)。基于小粒度的切片,進一步就可以實現多批次、多進程的并行推送,從而保證每個commit和時間單元的推送規模都做到failed范圍可控、同步進度可視。
遷移前準備:工具開發 & 測試
- 轉發入庫組件
在跳轉服務器起守護進程,監聽特定端口請求,再“bond”到下一跳服務器。邏輯比較簡單,偽代碼如下:
public void run() {
try{
ServerSocket serverSocket = new ServerSocket(local_port);
while (true) {
Socket socket = serverSocket.accept();
ProxyWorker worker = new ProxyWorker(socket, remote_ip, remote_port);
worker.start();
}
}catch(Exception e) {
//====異常處理
}
}
從實現的角度看,再網絡設備上配置轉發的話,效率應該會更高,但是客戶只扔給你服務器,其它讓你自己實現。誰讓我們是乙方呢。不過從后來的效果上看還不錯,基于自主實現的轉發機制,讓我們可以記錄每一個數據庫連接的時間消耗、“看到”當前連接,為更細粒度的負載分析、troubleshooting等提供了更多可能。
- 割接工具
所有可能要遇到的操作,即使是非常簡單的一句命令,能固化的堅決固化,能批量執行的堅決批量執行,因為對割接來說,最寶貴的是時間。特別是遺留系統,配置不一致的地方很多,開發工具的過程,也是檢查、提升系統一致性的過程。
系統配置收集器;
轉發路徑監視( 外網跳板到內網跳板、跳板到數據庫等);
割接前預配置/割接后檢查工具;
連接 切換 & 回退 工具等(事實將會證明每一行回退代碼都是非常必要的);
測試工具(批量插入/刪除數據,對比分析SQL執行的時間長度);
制作割接后檢查驗證清單。
- 高仿真測試
為了在遷移之前確認新庫的可用性,我們采用了雙庫并行的策略。
即在所有采集服務器開啟兩個入庫進程,讓一份原始結果同時入兩套數據庫。最大限度在沒有額外測試系統的條件下,利用現有資源,模擬仿真正式生產環境的并發壓力,提前優化新庫參數設置,也同時完成負載均衡、單點故障驗證測試。
并不是所有的程序都能輕易的實現雙庫并行,有的可能只要稍微調整配置文件,有些可能就必須修改代碼,還有的可能就做不到。從這個角度觀察,第一種應該就是好的代碼,靈活適應各類場景。靈活性低的應用程序往往缺乏設計,甚至都沒有做到配置-代碼分離,存在大量侵入式編程等。
沒有一帆風順
第一次割接失敗了!
失敗的體驗
第一次割接之后,系統各項功能正如我們預計的那樣順利運行。就在我們準備慶祝成功的時候,幾項關鍵業務的吞吐量卻急劇下降。初步判斷是性能問題,因為每次連接時延飆升了100多倍,高達秒級,基本是不可用的。
人工排查幾次以后,看到了大量的掛起進程,一堆的鎖表。而且失敗來源遍布一多半的服務器。雖然不想承認,但是我們不得不做出回退的決定。
**萬事留后路 **
割接失敗是需要承擔很大壓力的。這次割接是大家都期待很久的,為了幾分鐘的操作,用戶和我們整個團隊都付出了很大的努力,調動了方方面面的資源參與進來。
如果說有什么能稍感欣慰的話,那就是我們遍歷了各種可能,幾乎成功,在不可知的情況出現之后,還能趕在割接窗口結束之前,快速回退。這主要得益于前期準備方案時,特別考慮了最壞的情況,認真演練了回退流程。
這種體驗與技術關系不大,來自于勇氣——無論是正確的,還是錯誤的決定。
幽靈進程
事后篩查發現,導致割接失敗的是一個監測程序——不在生產程序清單里面,沒有讀統一配置,自帶定時調度,零散分布在一些機器上,早已經被人遺忘。
新數據庫的版本是Oracle 11g 。為了提高安全性,防止暴力破解數據庫中用戶的密碼,Oracle默認提供了一種機制:延長失敗嘗試響應。它的策略是:在連續使用錯誤密碼反復嘗試登錄時,從第四次錯誤嘗試開始,每次增加1秒的延遲,最長延遲目前是10秒。使用這種手段可以相對比較有效的防治用戶密碼的暴力破解。
第一次割接后,歷史遺留程序瞬間就觸發該機制,導致應用不可用。
割接后
我們后來是如何發現幽靈進程的呢?
沒有什么絕招。單曲播放:“完善配置檢查工具->模擬執行->發現解決問題”。
直到你清楚地理解系統里的每一個進程,幽靈自然無所遁形。一切都是功到自然成。
最后的割接非常平靜。
總結
雖然這次的遷移不甚完美,事情本身也談不上宏大,簡短的一篇更不可能窮舉所有的問題和細節,
但是有幾點思考還是想和大家交流:
知識圖譜
就以數據同步技術為例,網上關于exp/imp,OGG , DBLINK等的資料可謂多如牛毛,然而對于新手來說并沒有什么用!因為這些材料頂多只能算縮略版單一技術手冊(基本套路:原理、安裝、配置、運行,簡單DEMO),無法展現不同工具在技術全局中的位置和關聯關系,更不要說真實現場中的系統架構和決策經驗部分。變通
開始設計方案時想到的幾個大難題,都是通過替代方案實現的:奇葩的內外網分離方案,與IT部門關于權限問題的艱難談判,數據復制過程中及時性的要求...... 真實割接過程中,現場壓力狀態的進退困境。到處都需要權衡、選擇。
決策是一件非常艱難的事情,受到多種因素的制約,最終的決策是一個各種利益妥協的結果。正如另一位資深哲人所說:“項目經理首先要學會變通。——瓦西里.楊” 誠如斯言,天下武功,無堅不摧,唯變不破。韌性
按照最初的方案,我其實并不負責這項工作,后來就算參與進來,也并不負責主導。應該說起初也有僥幸心理,希望有其他人來背這個鍋。為了這次遷移,前面已經生生嚇走了好幾撥人。從技術上分析,我以前沒怎么搞過數據庫,并不具備任何優勢。如果說還有一點可以憑借的東西,我感覺是韌性。面對未知的恐懼,敢于直面;面對不確定的方案,不斷在嘗試;面對失敗的后果,坦然接受。
事情能做成,感覺很好,我喜歡干成事的感覺。