探討:Database Migration

Database migration (別名為schema migration 或者 database change management),目前是由ORM/ODM來實現,並非由資料庫管理系統本身來實現,其概念主要目的是為了在 盡量不破壞(PS. 只是盡量,並非完全能夠避免破壞)原有表格資料的情況下,來將每一個時間點下的表格架構進行版本控制來讓這些表格架構能夠重複使用、可根據執行環境來進行測試、共享於整個團隊。

The goals of database migration software are to make database changes repeatable, shareable, and testable without loss of data

具體版本形式

在這裡的ORM/ODM會透過migration檔案來定義每一個版本所擁有的架構內容,根據內容形式可以進一步區分為

  • State based : 每一個版本是以狀態來描述,狀態會是以某些資訊或者類似形式的內容來代表每一個版本,狀態描述其版本下所具有的完整架構內容為何,接著實際做切換時,會以目前版本的架構和指定切換的架構之間的差異是如何來更改
  • Change based :每一個版本是以版本之間的差異,當前版本內容會是前一個版本和目前內容之間的差異,所以通常會以連續的版本內容來構成最終版本下所對應的完整架構,近似於git版本中的Snapshot機制,

具體版本形式-例子

  1. State based migration:在這裡假設直接以白話來當作狀態描述每一個版本的完整架構內容,而每一版即為每一個檔案,一開始我們有欄位1、欄位2、欄位3,那麼就會以檔案1來代表,若第二版會是增加個欄位4,那就會以檔案2來代表,若第三版會是移除欄位3,那麼就會以檔案3來代表,說完這三個migration檔案的緣由,那麼若我們想自由切換版本的話:
  • 若我們想切換成第一版的話,會拿目前版本和代表第一版的檔案內容相比差異是為何,並根據差異來切換
  • 若我們想切換成第二版的話,會拿目前版本和代表第二版的檔案內容相比差異是為何,並根據差異來切換
  • 若我們想切換成第三版的話,會拿目前版本和代表第三版的檔案內容相比差異是為何,並根據差異來切換
  • 後面版本依此類推
    1
    2
    3
    4
    5
    6
    migration 檔案1:
    欄位1、欄位2、欄位3
    migration 檔案2:
    欄位1、欄位2、欄位3、欄位4
    migration 檔案3:
    欄位1、欄位2、欄位4
  1. Change based migration:假設有三個migration檔案,一開始會使用檔案1來表示初始版本的架構,隨後第二版的實際內容是欄位1、欄位2、欄位3、欄位4,第二版與第一版相差了欄位4,所以代表第二版的檔案2的內容就是該差異-增加欄位4,最後第三版的實際內容是與第二版內容相差一個欄位欄位3,也就是說第三版只有欄位1、欄位2、欄位4,那麼檔案3就會以差異來紀錄-移除欄位3,說完這三個migration檔案的緣由,那麼若我們想自由切換版本的話:
  • 若我們想切換成第一版的話,就會直接拿檔案1來代表
  • 若我們想切換成第二版的話,就會拿檔案1和檔案2來代表切換成第二版並給予系統去轉換成第二版的內容
  • 若我們想切換成第三版的話,就會拿檔案1、檔案2、檔案3來代表切換成第三版並給予系統去轉換成第三版的內容
  • 後面版本數依此類推
    1
    2
    3
    4
    5
    6
    migration 檔案1:
    欄位1、欄位2、欄位3
    migration 檔案2:
    增加欄位4
    migration 檔案3:
    移除欄位3
  1. 總結一下:
  • State based migration:是代表每一個版本的檔案都儲存該版本的完整內容,當要切換指定版本,就直接透過對應獨立檔案來切換成對應的版本
  • Change based migration:是代表每一個版本的檔案都會儲存前一版和目前版本之間的差異,當要切換指定版本,就必須透過一連串的檔案合併才能切換成對應的版本

參考資料:
What are database migrations?

具體實現概念

不論其形式如何,ORM/ODM具體實現概念會是以版本內容增量為目標來實現版本切換,在這裡會是允許開發者以偏好程式語言來開發對應的migration檔案,當執行對應其migration檔案會經由ORM/ODM而轉換成對應資料庫語法(如SQL),接著再向對應的資料庫管理系統發送對應的資料庫語法,來建立對應的架構,其對應的語法會根據對應資料表格是否在資料庫且是否擁有著資料而變動:

  • 若沒資料的話,ORM/ODM就會以目前所拿到的migration檔案來構建對應的表格X’來存在暫存空間,並檢查資料庫是否有對應資料庫和是否有資料,若都沒有的話,會取代掉在資料庫上同名的表格X或者直接建立
  • 若有資料的話,ORM/ODM就會先以目前所拿到的migration檔案來構建對應的表格X’來存在暫存空間,並檢查資料庫是否有對應資料庫和是否有資料,若有的話,就盡可能從資料庫找到對應的表格X來獲取可以填入新表格X’的欄位資料,並將資料填入X’,隨後用X’來替代資料庫上的表格X。

具體實現概念-例子

首先在這裡先假設資料庫沒有存在任何資料表格,開發者為了建立表格1而用偏好語言X來定義migration檔案,也就是第一版的表格1,隨後給予ORM/ODM執行並轉換實際對應的資料庫語法來在資料庫管理系統執行對應的操作,其對應操作會是從下圖中的第二步驟開始,會先依照對應的migration檔案來建構對應的表格1’,並檢查資料庫上的有沒有表格1是有資料的,若沒有的話,會將表格1‘寫入至資料庫上並構成表格1

若接下來開發者想建立版本2的表格1,就會建立新的migration檔案來對應該版本的表格,並給予ORM/ODM執行和轉換對應的資料庫語法來向資料庫管理系統發送對應操作,其對應操作也會是從下圖中的第二步驟開始,首先一開始會依照對應的migration檔案來建構對應的的表格1’,並檢查資料庫上的有沒有表格1是有資料的,若有的話,會將表格1的資料盡量填入表格1’,在這裡會將apple、orange、noodle等資料寫入至表格1’,等寫入完成之後,就將表格1’去取代資料庫上的表格1。

Database migration 的開發環境切換

ORM/ODM通常會區分成三種開發情景來設定不同情景下的連線設定,分別為:

  • 開發環境(development environment):通常會以本地端的資料庫作為連線設定的基礎
  • 測試環境(test environment):通常會以遠端的測試用資料庫作為連線設定的基礎
  • 線上環境(production environment):通常會以正式上線用的資料庫作為連線設定的基礎

這三種環境下所能使用的版本通常都是共同的,這些版本的切換通常運用在開發環境和測試環境,因為這兩個環境所儲存的資料皆為彰顯是否實現功能開發以及其功能的效能是如何,資料本身並沒有太大的意義,但若應用於線上環境下,會因為儲存的資料皆為使用者相關的而使這些資料具有較大意義,進而無法輕易切換版本,原因在於ORM/ODM來處理欄位時會因為移除欄位而損失資料

ORM/ODM 破壞原有資料?

在切換版本的過程,難免會移除掉某些欄位X,在這樣子的情況下,ORM/ODM若切換至欄位X還在時的版本內容,其對應的欄位的資料並不會跟著復原,比如說拿以下架構來進行欄位上的減量:減少price這欄位的話

1
2
3
name  price   date
apple 10 4/13
orange 12 4/14

那麼就會是以下結果,

1
2
3
name  date
apple 4/13
orange 4/14

接著再回到減少price欄位前的狀態:price欄位還在時的狀態,price欄位的所有資料會因為ORM/ODM只支援增量上的版本而永久遺失

1
2
3
name   date price
apple 4/13
orange 4/14

Why 探討:版本內容增量為目標來實現版本切換?

從上述例子可以窺見 版本內容增量為目標來實現版本切換的ORM/ODM 在處理欄位移除時會有損失資料的可能。

在這裡這些ORM/ODM之所以只能以增量相關來管理表格架構,並非是因為實作上是不可行的,而是因為若以內容減量來管理的話,除了基本的暫存空間來存放未來要切換的版本架構以外,ORM/ODM或者資料庫管理系統是必須要替每一個版本額外備份或者花費CPU成本去計算哪些欄位資料要額外備份,來方便返回至資料移除前的版本,不論是選擇哪一樣,那時勢必會需要每一個版本都儲存著那時原有資料的內容,甚至像git系統那樣:每一個版本都儲存前一版的差異,但不管如何,資料庫所需要的空間大小並不像git所需要的空間較小,除此之外,還要考量到資料同步上的問題會不會因為過於龐大、非同步、網路問題而產生不一致

而內容增量管理的話,除了基本的暫存空間來存放未來要切換的版本架構以外,就不太需要像內容減量那樣需要額外成本來儲存和計算需要附加的資料,當然若還原至資料附加前的版本,也只需要表格上的替代(附加前的版本是沒資料)或者從其他欄位獲取其他資料就能完成(附加前的版本是有其他資料),而資料同步問題的話,難免會因為會與原表格的資料有所出入,比如說在搬移過程,原表格有被添加幾筆紀錄,但比起減量而言,由於本身不需要額外成本去儲存附加資料(換言之,資料轉移至目的地(目標表格)的距離比起減量而言還要短),所以不一致問題比較輕微。

總結一下:

  • 減量的話,除了基本的暫存空間來存放未來要切換的版本架構以外,會需要額外(時間和空間)成本來定義需要還原的資料是哪些以及儲存該資料;然後由於需要從另外空間將資源還原至原始表格上,多多少少會有不一致的問題,甚至由於還原資料過於龐雜而使這問題更加嚴重
  • 增量的話,除了基本的暫存空間來存放未來要切換的版本架構以外,並不需要(時間和空間)額外成本來存放需要(處理)附加的資料,取而代之的是從對應表格取出對應資料來放在暫存空間的表格上;然後由於需要從另外空間將資源還原至原始表格上,多多少少會有不一致的問題。