# 知識閱讀 - 如何沒有 Downtime 的更新資料庫樣式

在更新資料庫樣式的時候，會不會影響系統造成 Downtime，最重要的就是你的操作是否可以向下相容。
所以區分成兩個：向下相容、不向下相容

## Backward-Compatible Operations

- Add a table or a view：新加上 Table 和 View 對於舊的系統並不會有影響，所以不需要做額外的操作。但是要注意，不要在操作還沒完成就部署上新系統，那就會造成問題了。
- Add a column：添加欄位，也沒有舊系統沒有影響。但是要注意是否有 `NOT NULL` constraint，因為 `NOT NULL` constraint，因為過去的資料就都會是空值，如果有預設值的話就還好處理。如果沒有預設值的話，就建議用以下三種方式來添加 constraint
   1. 新增沒有 constraint 的欄位
   2. 執行 database script 將其初始值填入欄位
   3. 加上 `NOT NULL` constraint
- Remove a column that’s not used by the old and the new version of your application：此操作因為系統都不在使用該欄位，所以也是可以直接處理（當然前提是確定真的不需要了）
- Remove constraints：不影響系統服務的操作

## Backward-Incompatible Operations

針對這些操作，通常就是造成無法順利無 Downtime 的更新。會建議使用一些熱門的工具：Flyway 和 Liquibase。

- Rename a column, a table or a view：今天欄位、資料表和視圖的改名，看起來很簡單。但是卻是最常見造成無法向下相容的問題，主要發生在 Rolling update 的時候。因為同時舊系統還在使用舊的地方，新系統又上線，怎麼在更新過程中兩邊的資料可以是同步的。
    - Option 1: Sync with database triggers：如果你的資料庫支援 trigger 的話，那這個操作就比較好處理了。
        1. 新增新欄位並且將舊資料複製過去，新增 Trigger 將新進入的資料同步到新欄位
        2. 執行 Rolling update
        3. 刪除舊資料(Column、Table、View)和 Trigger
    - Option 2: Sync programmatically：如果你的資料庫不支援 Trigger 的話，那就會建議將系統進入 Read-only 模式，不然可能會在更新過程丟失一些資料。
        1. 新增新欄位並且將舊資料複製過去
        2. 確定新系統會從舊欄位讀取和同時寫入到舊欄位和新欄位 - 先稱其為 `new1`。因為此時舊系統依舊可以寫入新資料或更新舊資料，所以要確定 `new1` 要可以處理新舊欄位之後就執行 Rolling update
        3. 確保 `new1` 都更新後，就可以在更新一版只有處理新欄位的系統 `new2`
        4. 刪除舊資料欄位
- Change the data type of a column：基本上的操作跟 Rename 一樣，唯一的差別是需要更改資料型態
- Remove a column or table or view that’s still used by the old version of your application：需要更新系統才有辦法操作刪除

## Reference
- [Update your Database Schema Without Downtime](https://thorben-janssen.com/update-database-schema-without-downtime/#Add_a_table_or_a_view)
  

