數據庫版本控制完全指南

jopen 9年前發布 | 14K 次閱讀 數據庫

在這個充斥著大數據與商業智能的新代時,唯一不變的技術就是變化,尤其是在數據庫方面。出于數據統計、繼續增加的對服務的需求,以及規定制度等方面 的原因,幾乎每天都有業務方面的變更需求,這些都會對數據庫產生變更需求。當數據庫變更發生時,能否從自動化中獲得更大的敏捷性,以較少的資源實現較多的 功能,正是那些具有高度競爭力的世界級企業在蕓蕓眾生中脫穎而出的關鍵因素。

如果你的競爭對手能夠更快地、并且交付質量更好的特性,那么 你必然會失去市場份額。敏捷開發方法的出現正是為了在應對不斷變化的需求的情況下快速地發展,在有限的資源下也能夠確保理想的質量。

重量級發布的方式已經過時了,為了每次更新或發布要等上足足六個月,這種方式無異于自掘墳墓。敏捷開發方法減少了每次發布的范圍,換取的是更快地完成每個變更,并且將每個變更的影響降至最低。對于技術公司與IT部門來說,必須以敏捷性來保證對不斷變化的業務需求的支持。


</div> </div>

接下來的一個邏輯步驟是將開發與運維相結合,即采用DevOps方法。

為了在敏捷的Sprint發布中有效地應用DevOps,你需要實現部署與流程自動化,自動構建內部的開發與QA環境,以及生產環境。否則的話,你只能選擇手動實現部署與發布的每個步驟與流程,這就很可能產生人為的錯誤,而且也無法頻繁地重復這一過程。

實現自動化依賴于版本控制系統,它能夠管理所有等待構建并部署到下一個環境的軟件資產。

 數據庫版本控制完全指南

構建流程的第一個步驟是清理工作空間,并從版本控制庫中換取相應的文件。這一重要的步驟避免了流程外的變更。如果開發者直接將變更保存到構建服務器 的工作空間,而不是將變更簽入版本控制庫中,那么這種變更仍然有可能產生。這個例子聽起來似乎有些可笑,因為開發者都明白,如果不把變更簽入到版本控制庫 中,這些變更就會丟失,因為技術手段保證了流程的正確性。這一步驟同時也避免了將尚未完成的變更包含至構建過程中,只有通過正確的簽入流程提交至版本控制 庫中的變更才會加入構建過程。版本控制庫在這里成為了唯一的信賴源

數據庫是關鍵的部件

如今多數的IT應用程序都包括數量眾多的部件,使用了多種不同的技術:移動、ASP、PHP、應用程序服務器、Citrix及數據庫等等。為了讓應 用程序正確運行,必須讓這些組件相互配合。下面舉個例子,如果在某張表中加入了一個新的列,或是在某個存儲過程中加入了一個新的參數,那么為了讓功能正常 運行,其它所有的應用程序組件也必須與結構的變化進行同步。一旦同步過程出錯,應用程序在調用存儲過程時使用了錯誤的參數,或者是在插入數據時遺漏了新的 列,應用程序就會出錯。

數據庫組件的獨特性將它與其它組件區別開來:

  • 數據庫不僅僅只是SQL腳本,還包括了表結構、在存儲過程中用數據庫語言編寫的代碼、在引用表或配置表中所存放的內容,以及對象之間的依賴等等。
  • 數據庫是集中式的資源,多個開發者可以在同樣的對象上進行工作,因此必須對他們的工作進行同步,以避免代碼相互覆蓋。
  • 數據庫變更的部署不像拷貝與替換舊版本的二進制文件那么簡單,在將數據庫從版本A轉換至版本B的過程中,需要在保留業務數據的同時轉換為新的結構。
  • 數據庫代碼直接存在于數據庫中,并且可以在任何環境中進行直接修改。這一點就與其它的組件不同,它們都是在構建服務器中的某個干凈的工作空間中進行編譯的。
  • </ul>

    必須滿足的需求

    在管理數據庫變更時,需要克服一系列的困難,你必須做到以下幾點:

    1. 確保所有的數據庫代碼都被涵蓋(結構、代碼、引用內容和授權等等)
    2. 確保以版本控制庫作為唯一的信任源
    3. 確保被執行的部署腳本在運行時能夠正確判斷環境狀態
    4. 確保部署腳本能夠正確處理與合并沖突
    5. 只為相關的變更生成部署腳本
    6. 確保部署腳本了解數據庫的依賴項
    7. </ol>

      對于在開發階段,以及內部部署(開發環境與QA環境)或部署到生產服務器時的數據庫變更管理,有四種通用的管理方式。

      1. 建立開發階段生成的SQL變更腳本
      2. 建立一個變更日志的跟蹤系統
      3. 建立簡單的比較與同步機制
      4. 建立一個數據庫執行變更管理解決方案
      5. </ol>

        建立開發階段生成的SQL變更腳本

        管理數據庫變更的最基本方式,就是將所有變更命令保存在一個或一系列腳本中,并且在基于文件的版本控制系統中對它們進行管理。以此保證在一個單一的 存儲庫中保存所有的應用程序組件資產。對于開發者來說,將數據庫變更進行簽入可以使用的功能類似于他們簽入.NET或Java的變更時的功能,例如將變更 與變更原因(變更請求、缺陷編號、用戶故事等等)相關聯。對于當前流行的各種基于文件的版本控制方案來說,基本上都能夠在多個開發者對同一個文件進行變更 的時候發出合并的警告。

        但讓我們來看一下,這個解決方案是否真正能夠克服數據庫方面的各種挑戰,并且避免各種可能的風險呢:

        • 確保所有的數據庫代碼都被涵蓋 —— 由于開發者或DBA編寫了腳本,因此他們能夠確保所有數據庫代碼都被涵蓋。
        • 確保以版本控制庫作為唯一的信任源 —— 并非如此。因為開發者和DBA能夠直接登錄(任何環境的)數據庫,并且直接在數據庫中進行變更。
        • </ul>

           數據庫版本控制完全指南

          手動編寫的SQL腳本

          對于部署腳本的變更,例如發布內容范圍的變更、分支合并及重復勞動等等必須手動完成,并且需要額外的測試。

          這種情況下需要維護兩種類型的腳本,即對這次發布的創建腳本,以及針對某些特定變更的變更腳本。對于同樣的變更需要維護兩種腳本,這已是災難開始的前兆了。

          • 確保被執行的部署腳本在運行時能夠正確判斷環境狀態 —— 這一點取決于開發者,以及腳本編寫的方式。如果腳本本身只包括相關的變更命令,那么它對于執行時的環境狀態就一無所知。這就意味著即使某個列已經存在,它也會試圖再次新增這個列。而如果要編寫能夠在執行期判斷環境狀態的腳本,會極大地提升腳本開發工作的復雜性。
          • 確保部署腳本能夠正確處理與合并沖突 —— 雖然基于文件的版本管制系統提供了合并沖突的功能,但這一點對于數據庫來說意義不大,因為版本管理庫里的內容未必是百分之百準確的,因此也無法充當唯一的信任源。腳本或許覆蓋了另一個團隊所做的某個hot fix,而這種錯誤不會留下任何痕跡。
          • 只為相關的變更生成部署腳本 —— 腳本的創建是屬于開發過程的一部分。根據所布置的任務,如果要確保腳本中只包括相關的、并且經過授權的變更,必需對腳本進行改動,而這進一步提高了部署時的風險,并且也會浪費時間。
          • 確保部署腳本了解數據庫的依賴項 —— 開發者必須在編寫腳本時留意到數據庫的依賴項。如果只使用一個唯一的腳本,那么變更通常是按順序從腳本的最后加入的,這就有可能導致對相同的對象進行多次變更。而且如果要使用多個腳本,那么腳本的執行順序則至關重要,并且必須手動維護。
          • </ul>

            結論:這種基本方式不僅無法克服數據庫的各種挑戰,并且極易出錯,也極耗時間,并且需要引入一個額外的系統以跟蹤被執行的腳本。

            建立一個變更日志的跟蹤系統

            另一種常見的方式是使用XML文件作為一種抽象的語言,對變更進行描述并對執行過程進行追蹤。這方面最常見的開源解決方案就是Liquibase。

            Liquibase使用XML文件將邏輯變更從物理變更中分離出來,并且允許開發者在不了解數據庫的特定指令的情況下編寫變更。在執行期間,它會將 XML轉化為特定的RDBMS語言以執行這些變更。所有的變更將被組合到一個變更日志中,日志可以作為一個單獨的XML文件存在,也可以是由一個包含了變 更順序的主XML文件所引用的多個XML文件共同實現。

            可以用現有的基于文件的版本控制系統保存這些XML文件,這一點與基本方式的好處是相同的。此外,通過Liquibase的執行跟蹤能力,還能夠了解到哪些變更日志是已經被部署過,不應該再次運行的,以及哪些是尚未部署而等待運行的。

            那么讓我們來看一下,Liquibase是否解決了這些挑戰呢:

            • 確保所有的數據庫代碼都被涵蓋 ——Liquibase中的XML文件不支持對引用內容變更的管理,必須由外部的擴展功能進行處理,這就很可能導致某些變更被遺忘。
            • 確保以版本控制庫作為唯一的信任源 ——Liquibase本身沒有任何版本控制的功能,它依賴于第三方的版本控制工具對XML文件進行管理。因此,你還是必須想辦法保證基于文件的版本控制 庫能夠正確地反映當前被測試的數據庫版本。為了確保版本控制庫能夠作為唯一的信任源,開發者必需將變更簽入,以便進行測試。這有可能導致尚未完成的變更也 被部署到下一個環境中。
            • 確保被執行的部署腳本在運行時能夠正確判斷環境狀態 ——Liquibase知道哪些變更日志已經被部署,并且不會再次執行它們。但是,如果某個邏輯變更是增加一個日期類型的列,而該列已經存在,并且是varchar格式的,那么部署肯定會失敗。此外,Liquibase也無法避免外部進程對數據庫進行的任何變更。
            • 確保部署腳本能夠正確處理與合并沖突 ——在Liquibase之外對數據庫進行的任何變更都可能導致沖突,而這是Liquibase無法處理的。
            • </ul>

               數據庫版本控制完全指南

              無法處理外部進程產生的變更

              • 只為相關的變更生成部署腳本 —— 在變更日志這一級別可以忽略某些變更,但將一個變更日志分解為多個日志需要重寫編寫XML文件,而這也需要更多的測試。
              • 確保部署腳本了解數據庫的依賴項 —— 在變更日志XML文件的編寫過程中,需要手動維護變更的順序。
              • </ul>

                結論:使用能夠追蹤變更執行的系統并不能處理數據庫開發中的所有挑戰,最終也無法勝任部署的需求。

                建立簡單的比較與同步機制

                另外一種常見的方式是通過將源(開發)環境與目標(測試、UAT、生產等等)環境進行比較,由此自動生成數據庫變更腳本。這種方式節省了開發者與 DBA的大量時間,因此他們無需手動地對每次發布的創建腳本或變更腳本進行手動維護了。只在需要的時候生成對應目標環境當前結構的腳本。

                讓我們再來看一看,這種方式是否能夠應對數據庫管理的挑戰:

                • 確保所有的數據庫代碼都被涵蓋 —— 多數的比較與同步工具都了解如何處理不同的數據庫對象,但其中只有一部分工具能夠在比較與同步時處理引用數據。
                • 確保以版本控制庫作為唯一的信任源 —— 簡單的比較與同步工具在執行比較與生成合并腳本的時候,并不會用到代碼控制庫。
                • 確保被執行的部署腳本在運行時能夠正確判斷環境狀態 —— 最佳實踐是在準備執行的時候生成腳本,這樣就能保證它引用了正確的環境狀態了。
                •  數據庫版本控制完全指南 確保部署腳本能夠正確處理與合并沖突 —— 簡單的比較與同步工具將A與B(源與目標)環境進行比較,基于右方的表,該工具能夠生成一份腳本,將目標環境進行“升級”,以符合源環境的內容。如果不了 解某個變更的內容,那么有可能會生成錯誤的腳本。舉例來說,在目標環境中有一個索引,是在某個不同的分支或是嚴重缺陷修復時創建的。如果該索引并不存在于 源環境中,那么工具又該怎么做呢?刪除這個索引?如果在開發環境中存在某個索引,而在生產環境中不存在,是意味著開發環境中加入了這個索引,還是說生產環 境中刪除了這個索引呢?使用這種工具作為解決方案,需要你對每個變更的內容有深入的了解,以保證能夠正確地進行處理。
                • 只為相關的變更生成部署腳本 —— 比較與同步工具會對整個數據庫schema進行比較,并顯示出不同之處。但它們并不了解變更背后的原因,因為這些信息是保存在軟件生命周期管理工具、 CMS,或是版本控制庫中的,而它們對于比較與同步工具來說屬于外部信息。結果是你可能會被一大堆無關的背景雜音所干擾,導致你難以判斷應該做些什么。
                • 確保部署腳本了解數據庫的依賴項 —— 比較與同步工具能夠了解數據庫的依賴項,并且以正確的順序生成相關的DDL、DCL與DML語句。但不是所有比較與同步工具都支持在生成的腳本中包含多個schema的內容。
                • </ul>

                  結論:比較與同步工具能夠滿足這些必要需求中的一部分,但不是全部。腳本依然需要手動審查,而且在自動化過程中無法完成依賴。

                  建立一個數據庫執行變更管理解決方案

                  數據庫執行變更管理結合了對數據庫對象強制使用版本控制的流程,并且基于版本控制庫及當前環境的結構,在需要時生成部署腳本。

                  這種方式意味著“按需構建與部署”,意即部署腳本是在需要時才進行構建(生成)的,而不是作為開發過程的一部分。這種方式保證了有效地處理沖突、合并,以及外部進程產生的變更。

                   數據庫版本控制完全指南

                  按需構建與部署

                  那么數據庫執行變更管理解決方案又是如何應對相同的挑戰的呢?

                  • 確保所有的數據庫代碼都被涵蓋 —— 結構、用數據庫語言編寫的業務邏輯、引用內容、數據庫權限等內容都被正確地管理。
                  • 確保以版本控制庫作為唯一的信任源 —— 強制的變更策略能夠阻止任何人在任何IDE(甚至是命令行)中對數據庫對象進行更改,而不經過事先的簽出與變更后的簽入。這就保證了版本控制庫在對象簽入時始終于對象的定義相一致。
                  • </ul>

                     數據庫版本控制完全指南

                    單一的流程強制了版本控制的實施

                    • 確保被執行的部署腳本在運行時能夠正確判斷環境狀態 —— 按需(在準備執行前)構建(生成)部署腳本的方式確保了它完全了解當前的環境狀態。
                    • 確保部署腳本能夠正確處理與合并沖突 —— 在分析過程中使用基線比較,就能夠了解變更的原因,并且能夠簡單地判斷是否要將變更進行部署、或是對目標環境進行保護(即忽略該變更)、或是對沖突進行合并。
                    • </ul>

                       數據庫版本控制完全指南

                      了解基線的分析

                      • 只為相關的變更生成部署腳本 —— 與應用程序生命周期管理(ALM)工具及變更管理系統(CMS)的結合保證你能夠為每個變更分配一個原因,就如同你在基于文件的版本控制系統或任務管理系統中所做的一樣。
                      • 確保部署腳本了解數據庫的依賴項 —— 嚴密的分析與腳本生成算法確保了DDL、DCL和DML等語句能夠根據數據庫的依賴,以正確的順序進行執行,包括了跨schema的依賴。
                      • </ul>

                        除了這些必需滿足的需求之外,還存在著一些別的需求,例如對并行開發的支持、合并分支、與數據庫IDE的集成,以及支持由數據建模工具生成的變更等等。無論你選擇了哪種方式,都必須驗證它是否能夠處理這些需求。

                        結論

                        對數據庫組件的管理有著特殊的需求,因此對自動化流程來說是個極大的挑戰。在距今較遠的年代里,一年中通常只有幾次發布,因此花費大量時間對數據庫 部署腳本進行手動審查以及維護是常見的、也是情有可原的做法。現如今,隨著對敏捷及更快的發布速度的需求不斷增長,數據庫管理必須成為自動化流程的一部 分。無論是編寫SQL或XML腳本,或是使用簡單的比較與合并工具,一旦在自動化過程中使用,都存在著低效或是高風險的問題。最有效的方式是實現數據庫執行變更管理

                         數據庫版本控制完全指南

                        關于作者

                         數據庫版本控制完全指南Uri MargalitDBmaestro公司的產品管理主管,Dbmaestro是一家企業級軟件開發公司,致力于數據庫開發與部署技術。Uri在企業軟件與系統管理方面有著15年以上的經驗,并且曾在多家處于業界前沿的軟件公司中擔任過高級產品經理與研發經理的職位。

                        查看英文原文:The Definitive Guide to Database Version Control

                        來自:http://www.infoq.com/cn/articles/Database-Version-Control

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!