Allen在發布了關系型數據庫開發的三個原則的經驗總結文章之后,就開始寫后續的系列文章了。這三個原則如下:
一、不要在共享數據庫服務器上進行開發工作
就像軟件開發中其它所謂便捷的方法一樣,共享數據庫的使用也是一個泥潭,它正等著凍結一個項目呢。開發人員相互覆蓋彼此所做的修改,我在服務器上所做的改變讓你的開發機器上的代碼中斷運行,這些都讓遠程開發速度很慢而且非常困難。避免使用共享數據庫,也就免避了因使用它造成的極度時間浪費和因之而生的Bug。
二、僅保留一份權威的Schema生成源
每一個人都必須知道該從哪里獲得正式的Schema,并且可以用它輕松地重新創建一個新的數據庫。當我走到電腦前,可以從源碼庫中獲得最新的版本,構建后就可以通過最簡單的工具創建數據庫(在更多的場景中,構建的過程甚至可以在數據庫不存在時自己創建一個,所以這個構建過程應該是一步到位方式的)。
三、對你的數據庫進行版本管理
這樣做的原因之一就是要將變化由開發傳遞到測試,最終在一種可控制的、一致的環境下生產。其二就是可以重建任何時間點上的數據庫,如果你正在將軟件交付給客戶的話,這一點就尤為重要。如果有人在你提交的應用版本build 20070612中發現了bug,你就必須能重建當時那個版本的狀況——包括數據和其它所需。
Allen說明了版本化數據庫的目的就是為了能保證所做的改變能保持一致性、可控性、可測試性和可重現性。許多推廣者都同意這一點,并認為實現這個目標對任何一個敏捷團隊的效率都很重要。
在列出了版本化數據庫的重要性后,Allen又相繼發布了4個貼子來描述他推薦的實現方法。
其中,第一篇貼子描述了Allen宣稱的版本化數據庫的起點——創建一個數據庫Schema基線。從本質上來講,這個基線是一個腳本,或者是一連串腳本,它包含所有可以從零開始生成應用數據庫的SQL命令。它包括創建所有數據庫所有對象(表、約束、函數、視圖、索引等)的SQL命令、表查詢及操作命令和插入應用所需初始數據的命令。Allen建議,一旦它完成創建并且驗證無誤,應立刻“將它提交到源碼控制庫”,此時“你可以認為已將數據庫基線化了 ”。
對于如何創建這個基線,Allen建議使用那些能從現有數據庫中導出腳本的工具(與手工編寫他們的過程相反)。作為參考,他還描述了他是如何結構化那些生成的腳本文件的:
我喜歡將所有生成表、約束、缺省值和主鍵的SQL語句保存到同一個文件中,而那些創建視圖、存儲過程、函數的腳本則分開來單獨存儲。
如果你喜歡多文件保存的方式,那就需要一個批處理文件,shell腳本,應用程序,或其它自動化工具來自動定位并運行安裝數據庫需要的所有腳本文件。人工干涉這個過程是一種倒退。
Allen建議并強調,基線中需要一個表用來記錄任何有關數據庫結構的改變,在他后面的三個貼子中,他詳細描述了該如何處理這些變化。
首先,Allen討論了變更腳本——一種管理除視圖、存儲過程、函數以外的數據庫對象的機制。這種方法要求任何一個改變(或一組相關的改變)必須有一個新生成的腳本文件可通過“增量”更新的方式來代表,這與Ruby Migration很相似。換句話說,當團隊發現數據庫需要做改變時,他們創建一個新的腳本來將數據庫修改到想要的樣子,通過測試后提交到源碼控制庫中。一旦發布后,這個腳本就永遠不要再修改。
Allen這么做使視圖、存儲過程和函數的更新方式與其它數據庫對象完全相反,每個對象都有一個“創建命令”文件,然后通過更新這一個文件來更新這些對象,對于為什么他喜歡這樣做,他解釋到:
原因很簡單,就是為了更快速地確定問題所在。如果有人提交了一個數據庫結構變化,它移除了視圖所引用的一個列,那么你可以盡早地發現有錯誤,因為在構建版本提交到測試以前,這個問題就會被發現;同樣,如果有人提交了一個視圖,但卻忘了發布它所需要的結構改變,幾分鐘后就會有人跑到他們的桌子前問他們為什么要破壞軟件的運行。
另一個原因就是為了避免我遇到過的某些不太常見的錯誤。對于那些隱匿在視圖背后的Schema的變化,某些數據庫產品仍會強迫完成執行計劃,而由此引發的問題很難跟蹤。“扔掉所有的東西,重新開始”會避免發生這類事情。
Allen著重強調了利用自動化工具更好地實施上述策略的重要性:
當開發人員、測試人員或者安裝人員從源碼控制庫中更新并運行本地數據升級工具時,它就像會魔術般地完成工作。它有三個步驟:
1、通過對比現有數據庫結構變更腳本文件和SchemaChangeLog表中的記錄,來應用最新的數據庫結構;
2、刪掉數據庫中所有的存儲過程、視圖和函數;
3、運行所有的變更腳本將視圖、存儲過程和函數添回到數據庫中去。
對于遵循這些策略的好處,尤其是使用自動化工具,Allen給出了一些示例:
由于數據結構變更腳本保存在源碼控制庫中,你可以在任何時候重新創建任意時間點上的數據庫。如果客戶報告了一個關于build 3.1.5.6723的bug,那么,你所需要做的就是獲取相應版本標號或標記過的源碼,然后運行這一基線和這一標記下所有的數據庫變更腳本。當其運行結束后,你就已經有了一份與客戶發現bug時一模一樣的數據庫,也就獲得了一個重現這個bug的好機會。而且,當改變由開發階段進入測試階段時,就從根本上提供了一個一致的、有序的、可重現的產品。
Allen在這一系列文章的最后,還提及他是如何處理分支與合并的,而這是所有應用在版本服務器上建立它的第一個版本后都要面對的現實問題。 Allen建議為發布而分支,這也是他偏好的分支策略,同時解釋了他為什么會為每個新的發布版本重創數據庫基線。他通過一個示例來描述了這個問題,并添加了這樣一個場景:在較早版本中發現了缺陷,就必須對已分支的版本進行相應的數據構結構變更。在這個分支版本中,創建新的腳本來處理變更是沒有問題的,問題是,如何將這個變更也應用到當前的主線版本中:
想要在主線中修復它,有兩種選擇。實際上可能會有無數種可能性,這取決于你想如何應用你的更新。但這里只提供兩種選擇:
1、將數據庫變更腳本合并到當前主線版本01.00.0046的腳本中,并在基線版本2.0中進行相應的修復來處理這一變更;
2、寫一個新的數據庫結構變更腳本02.00.0003,其與分支版本46中的變更保持一致。
對于選項一,你必須小心處理,因為任何已經更新到v2.0版的數據庫都并不會從分支上獲得46號變更腳本(除非你編寫的工具與我的不一樣)。你只能讓別人手工運行這一腳本,或者你自己查看對與現存的2.0版本數據庫沖突(對于這種結果,無論如何僅限于在開發和測試機器上)。所以,除非你剛開始著手 2.0的開發不久,否則這個選擇并不算太好。
相比之下,選項二則要友好多了。1.0版本的數據庫將會從01.00.0046中獲得修復。2.0版本的數據庫則在02.00.0003中得到修復。但你也要很小心地去編寫02.00.0003的修改腳本,以免它履蓋運行01.00.0046腳本后所做的修改。
換句話說,數據庫是按照2.0版的基線腳本安裝的,必須要應用02.00.0003腳本,但實際的產品數據庫可能是從1.0版開始的,它將應用 01.00.0046腳本來修復,所以你不能讓02.00.0003去再次修改這個實際已經升級到2.0版的數據庫否則,會造成錯誤
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com