宇文湛泉,現任金融行業核心業務系統DBA,主要涉及Oracle、DB2、Cassandra、MySQL、GoldenDB、TiDB等數據庫開發工作。
這似乎是DBA工作中最為無聊、繁瑣以及最沒有技術含量的事情。這也許是DBA工作中,最可能一擊致命、產生巨大損失的工作內容。這部分內容雖然LOW,但是對于DBA工作來說,卻是基石般的存在。
從最樸素的角度上看,投產上線的數據庫版本只有兩部分,一部分是DDL文件,另一部分是在OS上運行生效DDL文件的腳本。當一個系統里的數據庫表并不多的時候,數據庫版本可以作為應用程序版本的一部分。數據庫建表、變更,由開發者或者運維者執行生效到生產環境即可。往往有經驗的開發者或者架構師,可以憑借豐富的經驗,一眼看出版本生效是否存在風險并給出回避方案。
當系統越做越大,變得復雜起來,數據庫表變得種類繁多,開發者的人數也越來越多。通常會有出現兩種選擇。一種是,誰的表誰管,即每次投產的數據庫變更歸對應的開發者,依然視為應用程序版本的一部分。另一種是,整個系統的所有數據庫變更,由某一個DBA來統一實施。
前一種方式,往往會產生各式各樣,五花八門的實施方案。因為每個程序員都有自己的開發習慣和喜歡用的腳本。如果這個系統,不僅復雜,而且還很重要。那么這個系統的架構師就不得不花加倍的時間,來復核這些五花八門的方案的正確性和風險程度,以確保投產上線的萬無一失。
這時候,另一種模式會有個顯著的好處,因為它有一個統一的實施工藝,這對于系統架構師或許會非常的重要。因為無論這個系統里面有多少數據庫表,架構師只需要審核有限種類的實施方法就行了。畢竟只有一個實施的DBA,怎么著也不可能做出太多的實施方案。不過顯而易見,這種模式對這個實施的DBA可不是什么友好的事情。我們來想象一下,若是一個DBA做一個超大規模的數據庫版本,可能會遇到什么?
一、版本準備階段
版本準備階段,這指的是上線范圍確認之后,到上線投產之前,做準備的階段。
1、手滑風險
如果一次投產,僅僅是新建一張表或者加一個索引。選擇手搓一個SQL語句并拍到環境里的情況,并不罕見。甚至不能說這有什么非常不合理。但是手寫數據庫DDL和生效腳本絕對是充滿了風險和不確定性的一件事情。也許在測試環境或者專門的版本驗證環境運行一次版本,能有效的擋住一些錯誤,但是絕對不會是萬無一失的。
舉個例子:比如說數據庫表的最后一個RANGE的范圍(000-999)手滑寫成了(000-998),湊巧測試環境,并沒有這條邊界數據,那么這個問題就極有可能在生產報錯時能才能發現。一旦一次變更的內容足夠多,那手工版本不出錯的可能性非常的低。想象一下,你面對100個手寫DDL文件時,你從第1個檢查開始檢查,在看第100個文件的時候,你是否還保持著你可靠的專注力,以確保你不會犯低級錯誤。
2、版本范圍準確性
當你面對很多的開發人員,在某一次版本,一次性變更100張甚至更多數據庫表時,你做完數據庫版本,難免會產生一種自我懷疑,“我有沒有可能漏了一個索引?”更要命的是,盡管這100個表當初都是你親自做的數據庫設計,評審的SQL語句。但是經過測試過程中,解決問題、調整方案。這時候其中的某一張表,到底有幾個索引,到底是什么使用場景,你可能都記不清楚了。這時候如何對整個版本的正確性有把握呢?
3、版本內容準確性
當上線前,你打開上百個DDL文件,任何一張表都可能就有幾十個字段加若干索引。假設你是一個記憶力不錯的DBA,你幾乎記住了每一張表的各種使用形態,清楚它的分區等存儲設計,了解哪些SQL使用哪個索引,甚至于你記得針對業務場景設計了一些特別參數,這些已經相當不易。
如果說開發的程序員,在測試過程中,調整了某一兩個字段的屬性。這時候還要讓一個超大系統的DBA,把這個細粒度級別的內容,都記得清清楚楚,是非常困難的。所以,超大型的數據庫版本,必然會需要核準機制來確認你上線的DDL是完全沒有問題的。最簡單的方式可能是,DBA需要確認投產使用的DDL于開發環境和各種功能、非功能測試環境的DDL一致。或者DBA根本就是用同一個DDL文件來實施所有的環境的。
4、警示機制
對于特別重要的數據庫表,或者影響面特別重要的交易,在投產準備的過程中,應該要有足夠的警示和應急預案。比如說在線DDL有可能導致線上交易卡頓。當版本中某一個數據庫表涉及到類似監管機構考核響應時長的交易時,在版本實施之前,DBA應該提前給出相關的警示。
二、實施階段
實施階段,這指版本實施的窗口時間段。
1、生效方式和流程
如果你要創建三張表,你有可能會寫三個腳本依次運行,也有可能會寫一個包含創建三張表的腳本一次性運行。這看起來好像沒什么區別。但是在大型版本的情況下,是完全不一樣的。我們先簡單的增加一下工作量,假設我們一個稍微復雜一點的數據庫表變更表中字段,我們大約需要以下幾個步驟:
- 數據導出
- 數據加工
- 數據庫表刪除
- 數據庫表重建、索引重建
- 數據加載
只要同時操作20張數據庫表,就有100個小步驟。如果每一個小步驟需要執行一個腳本,那么投產切換的步驟就需要執行100個腳本。這時候DBA基本上就不太可能選擇自己去手工運行這100個腳本了。那用一個腳本運行這100個DDL行不行?大概率也是不行的,因為在7*24的系統上,你可能要在有限的時間窗口里,完成大量的步驟,這大概率需要更合理的流程。
比如說數據庫表的導出、不同數據庫表的數據加工等操作可以同步并行執行。這時候,就需要考慮不同步驟之間的依賴關系。隨著多種變更方式混合在一起,可能有的項目新建表,有的是加索引等等,超大型的版本流程會變得復雜起來,而且不易于人類來記憶。這時候,DBA需要設計一個流程調度者,它可能是某個程序進程,來有條不紊的控制整體版本流程的高效生效。
2、異常報錯處置
即使在擁有一個完美的生效流程的前提下,也無法保證版本實施生效過程中,絕對不報錯。比如說,你的用戶缺少某個權限、數據加工中磁盤空間不足等等。這時候你設計的調度程序,需要能夠準確的識別異常,并可以隔離掉與這個步驟有關聯的步驟,同時保證與之無關的步驟還能繼續執行。
此外,在問題解決之后,異常相關的步驟還需要比較簡單的重新喚起機制。整個異常處理的過程,需要清晰的日志痕跡,方便快速的錯誤定位和事后復盤。
3、檢核機制
即使你建了100張表的超大流程完美運行,沒有任何報錯。你會不會產生一種懷疑,“我有沒有可能漏跑一個索引?”難不成執行完之后,去一個個DESC表來檢查運行結果?顯然不可能。所以超大型的數據庫版本一定會要配備生效后的檢核機制,它可能是針對數據或者是元數據的。
三、進階問題
除了這些之外,既然你是一個超大型系統的DBA,你所面對的肯定不僅僅是如此,當然接下面這些問題的本質與上面的問題還是一致的。
1、版本疊加
一個超大型系統,在一些公共或者平臺能力的數據庫表的修改上會有版本疊加的可能性,比如說程序員M,因為某個需求在某個表A上增加了字段COL1。同時程序員N,因為另一個需求在表A上增加了字段COL2。他們有可能在同一天,或者是前后差幾天的時間里面上線。M和N甚至可能在不同的環境開發和測試,他們有可能互相不知道對方需求的存在。
這時候,DBA需要在很早的時候,甚至是他們開發的早期,就具有識別版本可能疊加風險的能力。而不同項目組,使用了不同的測試環境,DBA還需要確保上線的數據庫版本,在多個不同測試數據庫版本同時存在時,依舊準確無誤。
2、量級性異常
系統一大了,奇怪的錯誤也更容易出現。因為很多時候,往往由于成本原因,超大型系統往往難以配備和生產環境一模一樣的硬件設備。減少測試環境的數據量,是非常常見的作法。那么測試環境的數據量遠少于生產環境時,就經常會因此出現問題。比如磁盤空間不足、磁盤吞吐能力瓶頸、執行計劃差異等等。還有可能是大規模的數據清理或者操作,引起諸如REDO 空間不足,BINLOG同步炸掉之類的問題。
四、我們怎么應對
幾乎每一個程序員,都會寫一些自己常用的工具代碼來簡化自己的日常工作。當然也包括DBA。這樣做的好處是顯而易見的,我們看看可以通過一些工具程序來做些什么:
工具程序會根據開發者的使用數據結構(或者企業級元數據字典),直接產生DDL,這可以非常有效的避免手滑問題。最重要的是,產生一個DDL文件和產生一萬個DDL文件,對于代碼來說,不僅僅花費時間幾乎是一樣,出現問題的概率幾乎是一樣的。
工具程序在數據庫評審的時候,就會根據項目錄入非常具體的設計信息和范圍,包括涉及的表、索引有哪些,SQL語句有哪些。并且在每一次設計調整時,我們都可以通過工具生效在測試環境之后,留下變化的記錄。那么,在某個投產日的項目范圍確定之后,整個系統的數據庫變更范圍隨之確定。這樣不容易上錯內容,也不容易缺少什么內容。
工具會根據不同的變更類型,使用標準化的流程。比如工具檢測到,投產內容是表的末尾增加字段,工具即產生對應的ALTER TABLE語句和運行腳本。再大再復雜的數據庫版本,它的生效流程種類總歸是有限的(當然也可以在一定的時間里,工具系統逐步完善和迭代)。程序總是能冷靜和準確的根據我們編碼出來的規則,選擇恰當生效方式和流程。系統架構師甚至都不用每一次上線都親自來復核版本是否靠譜,他只需要在這一套工具程序穩定運行之前,復核規則和代碼是否可靠即可。幾乎是一勞永逸。
異常處理往往是需要人工介入的,比如你加了磁盤、賦予了權限。但是異常處理完的后續工作,最好還是交回工具程序來處理。這里就需要程序具有斷點續跑和重跑的能力。工具程序需要清楚的識別哪些步驟執行了,哪些沒有執行,這比起DBA在萬千作業里面去判斷要重跑哪些腳本,先跑哪個再跑哪個要靠譜得多。
投產檢核其實通常并沒有什么特別復雜內容,版本末端的檢核程序可以比人類檢核更加準確、更加高效。
除此之外,最最重要的部分是這件事情。任何工具、流程可能確實無法達到完美的程度,但是它卻可以被固化下來,它是可持續積累的,這是它最大優勢。有一種情況是非常常見,就是同一個錯誤會反復發生。某一個錯誤在發生之初,可能會被程序員重視,并在接下來的幾次投產過程中被反復檢查。
但是,往往過了幾年之后,同樣的錯誤非常有可能再次發生。因為可能這中間的人力發生了變化。即使是同一個人,他也可能因為忘了,導致錯誤的重復發生。而針對某一個問題的代碼,一旦被寫到了工具程序中,這個問題再發生的概率近似為0。
五、我們的思路和原則
以上我們基本上只說了,我們需要做什么,并沒有說具體是怎么實現的。我們實現過程中的幾個原則是比較重要的。
1、零手工原則
這個原則即上線使用的所有DDL文件,執行腳本,均100%由版本準備工具程序產生,不允許手工編寫。整個版本的范圍,輸入信息應該僅僅是DBA選擇某次版本日上線的哪些項目。這些項目最終應該關聯多少DDL,并產生什么樣的生效腳本,均應該由程序生成。
2、一眼看清原則
大型的系統,在投產前,因為各種各樣的因素,調整上線范圍是經常發生的。所以,有時候還會需要DBA會去,檢核程序生成的代碼文件。需要人眼看的部分,一定要非常的清晰。切莫把生效用的腳本代碼和投產范圍相關表名、庫名之類的內容,在文件內混在一起。要進行清晰地封裝。
在上線版本包里,應該比較清晰的分為運行代碼部分和上線范圍部分。
運行代碼文件中,通常寫的是標準化的流程和程序運行的代碼,這部分一旦穩定之后,每次版本上線時,應該是固定不變的。另一部分,是每次上線都不一樣的部分,比如表清單等跟上線范圍相關的內容,簡明易讀。此外在版本生效過程中,輸出的錯誤日志,也需要非常清晰,以方便DBA去查看。這里也需要將流程的顆粒設計得非常細,在錯誤產生得時候,就可以快速精確的定位錯誤發生點。
3、傻瓜原則
在正常情況下,上線的操作步驟一定要足夠簡化。如果系統沒有異常狀況,應該是一個傻子都可以完成版本的正確生效。這個原則主要用于排除人為主觀因素,對投產帶來的風險。把復雜的事情,留在投產之前。上線的時候,不依賴于當時DBA的狀態和反應速度。我們會把整個版本生效簡化到運行一個入口腳本。異常處置時,我們也還是重新運行這同一個腳本。極致簡化,不給操作者犯錯的空間。
不得不說,數據庫版本質量控制,是DBA工作中我最不喜歡的部分。但是確實我最花心思的部分。上面的這些內容,無論思路或者程序的實現,其實并沒有很難。但是一個大型系統的DBA,對于每一次上線的數據庫版本,心理有把握很難。有100%正確把握,就非常非常難。
我們可能需要幾年,甚至于整個系統運行的周期,都在不斷迭代,修補我們的工具程序,來堵住各種意想不到的漏洞,直到我們的版本質量,越來越接近完美。
直播預告丨三位大數據專家齊聚,探討實時計算、數據湖、數據治理、平臺化建設與實踐
隨著企業業務規模的不斷擴大,數據分析處理的準確性和實時性要求也逐步提高。因此,建設兼顧效率和質量的大數據體系成為了業界的共同課題。為此,dbaplus社群攜手愛奇藝三位大數據專家,圍繞“愛奇藝復雜場景下的大數據體系建設與實踐”這一主題開展線上直播分享,針對實時計算、數據湖、數據治理、平臺化建設等議題進行深入探討,給大家提供企業級大數據體系建設管理經驗參考。