只有累積,沒有奇蹟

2020年5月17日 星期日

[筆記] TGONext 架構組 - 資料庫 Migration 及軟體架構的演進

前言
這一篇是紀錄參加 TGONext - TGONetworks 導師計畫架構一組第三次聚會的旁聽筆記,導師計畫歡迎小組間跨組討論因此自己只要時間允許會參加其他組的討論,參加完後也會整理討論的筆記方便日後回顧,在過去兩個月有參加的架構組筆記分別如下 為了日後讓自己可以更快理解因此整理筆記時有加上自己理解的說明與圖片,其中若是自己有理解不正確的地方是錯誤的部分歡迎各位高手一同討論或給予指導

Database Migration
在這次聚會的一開始同學拋出 Database Migration 的議題,程式碼有版本控制系統,遇到異常狀況時可以找到原本執行正常的程式碼快速進行 Rollback 的動作,那麼資料庫遇到類似狀況要如何 rollback 呢? Ant 請同學分享針對過去的經驗或是解法,可能的解決方案如下
  • 不刪除資料及欄位
  • 保存刪除資料,使用 Trigger 將資料複製到另一個 Table
  • 將要異動的 Table 建立一份 snapshot 快照
  • Rename & Copy
這問題沒有一定標準的答案,導師也提醒需要考慮在特定的法規中需要保留資料特定年限才能刪除,像是在金融業政府就規定交易紀錄須保存五年以上,方便稽核單位進行查核的動作。
導師 : 如果不刪除資料的話,大家覺得會有什麼問題?
如果資料庫有很多非必要資料或是欄位時,如果不刪除的話會占用 disk 或是 memory,在傳輸時會多占用網路頻寬;存放過多 Dirty data,後人在維護時也有可能因為不敢亂刪除資料或是欄位,因而繼續存放演變為技術債,也可能會讓 db 的 loading 越來越高,因此可能會影響到資料庫的效能,為了不影響資料庫的效能處理方式有很多種,常見有兩種方式處理
  • 將沒用到的放置在另一個 Table,如果之後 Database 需要 Migration 時可以將所需要資料移回來
  • 將不需要用到的資料 snapshot,有需要 Rollback 時再將 snapshot 資料回復到原本的資料庫中
Add New Columns
RDBMS 是 row base database,新增一個欄位所需要的時間取決於目前資料量的多寡,如果目前異動的 Table 有兩千萬筆 row data 時,這兩千萬筆資料都需要新增 columns,在 Production 的 Table 新增欄位會有 lock 的情況發生,也就是說可能會有 downtime 的狀況產生,在過去的經驗如果要在核心 Table 進行異動或是 index rebuild 的話,會在網站者使用者相對較少的時間進行停機告知用戶網站正在維修中(Under Maintenance),在交易量大的網站中對於 downtime 是格外重視的,在 PostgreSQL 11 與 MySQL 8 分別都有支援新增欄位不會 Table lock 的方法。
那麼不停機要新增欄位,要怎麼做會比較好呢? 
在 MySQL 有工具可以協助達到此目的 : gh-ost,原理如下 了解運作原理之後,那麼它可能的缺點會是什麼?舉例來說目前資料庫中有些 Table 近 2G 的空間,會進行 COPY Table 資料,可能問題會是 COPY 原有 Table 資料過程中,由於資料量非常龐大此時 Disk IO 會飆高,cpu high或是既有空間會不足的情況發生,這都是使用前需要留意的地方。

Row Based Database vs Column Based Database
在比較 RDBMS 與 NoSQL 的特性時,導師也提到可以從 Row Based Database 與 Column Based Database 的設計進行研究,這方面比較不熟因此列出網路上文章日後有時間拖稿再繼續研究

資料結構 : Tree
接著討論有關 RDBMS 資料庫與 NoSQL 本質上的差異,以及資料庫中的重要觀念 B Tree & B+ Tree,內容相信大家都了解這裡就不在說明,這裡列出幾個自己覺得說明蠻清楚的網站讓大家了解其原理

軟體架構演進
接著回到軟體架構的討論,在上次的討論中有提到 架構的演進,在討論的一開始導師又拋出問題給同學
導師 : 當單機承受不住的時候,為了支持更大的量,你會怎麼做 ? 

  • 機器硬體加大
  • 橫向擴充
  • 服務拆分
  • Cache Server
  • Queue
導師強調演進沒有一定的答案,每個解決方案都有適合他的情境,架構也有各自的瓶頸點,舉例來說選擇使用垂直擴充加強機器的硬體,但升級並不是無限擴充的會有物理的上限問題;如果選擇橫向擴充了100台機器,機器的管理就是一個問題,像是如何將新功能在最短時間佈署至100台機器,佈署策略要如何去定義,分散式系統的問題開始浮上檯面不得不面對;在上次聚會中提到 cache server 可能是其中的一種方法,但是團隊是否有能力可以處理 cache 帶來的問題,要如何處理因為 cache 異常引起的雪崩效應,常用的 cache 有 Redis 與 Memcached 兩種,Redis 相信大家都不陌生,在 Facebook 與 google 仍然在使用 Memcached,導師也請同學們思考看看背後的原因。

擴充方式
  • Scale up : 垂直擴充 (Scale vertically),提升單體 Server 硬體效能,像是增加 CPU 核心數、硬碟換為SSD、升級記憶體 Memory、升及網路卡速度,硬體升好升滿的概念。
  • Scale out : 水平擴充 (Scale horizontally),增加伺服器數量,使用更多機器一起來負擔更多的用戶與 Request 請求數量
  • 但在垂直擴充到某個程度可能會有硬體的物理上限,相對的硬體資源更好的伺服器費用也較為昂貴,
    使用大量硬體較便宜伺服器來實現提升整體效能成本上會來的較為便宜,機器異常時造成服務中斷的風險也比較低。

    Database 擴充
    在 RDBMS Database 相對於 Application 來說,擴充上是較不容易的(不考慮分散式系統或是讀寫分離的情況),如下圖所示

    此是一個簡易的前後端分離架構圖,當用戶透過瀏覽器發送請求到資料庫處理的流程來區分,網路可以透過 CDN 快取加速,前端 FrondEnd 網站與 Backend API 可以透過 local cache 或是 Redis 等快取機制加快處理速度,當 Server 機器數量不夠時可以透過 Scale out 擴充承接更大量的 Request 請求,前面假設機器加到 50 或 100 台 server 能夠應付更大的流量,最後在做資料異動還是需要透過 Database 來處理,由於 RDBMS Database 的特性(Atomicity、Consistency、Isolation、Durability) 注重資料的一致性,因此在擴充上是較難分為多台水平擴充,如果遇到效能的問題需要做擴充大多都會選擇硬體升級方向出發,但是升級都會遇到機器物理的上限無法無止境的提升,因此保護好資料庫變成一件很重要的事情。

    資源的限制
    在架構的選擇中,資源(Resource)也是考量的重點之一,常見的資源有 CPU、Memory、Storage space、Disk IO、Network bandwidth、Network latency 等,請大家思考哪一個項目是覺得比較難擴充的,有人回答 Disk IO,因為 Network 的問題或許可以靠 CDN 去解決,但 Disk IO 可能跟不上 CPU 的速度,大家都提出自己的觀點與看法,接著導師請大家思考
    導師 : 在雲端服務廠商裡面,上述提到的(Resource)有什麼服務是不能選擇的 ? 
    導師提出他的觀點 Network 是無法選擇的,Google 推出 Protocol Buffers 以及 MessagePack 等新的 format 就是為了解決網路傳輸的問題,主要是透過(使用CPU)壓縮的方式讓傳輸資料輕量化,以加快資料網路傳輸的速度,也就是用 CPU 去換傳輸資料輕量化。

    Queue
    導師 : 甚麼時候應該要用 Queue ? 它的特性應該是什麼 ? 
    • 有順序性,存放暫存的請求內容(狀態state)資料
    • 在架構設計上常會用於系統與系統間解偶(Loosely Coupled)
    • 像是非同步,處理完畢的時間不確定 (asynchronous 的缺點都有)
    使用上要注意的是要有(限制) 容量 (memory) 的上限,不能是無限制的使用;另一點要注意的是當 producer 要將請求內容放置到 Queue 的時候,如果遇到 Queue 已經滿了放不進去時該如何處理 ? 遇到 Queue 滿了是否有將 producer 的內容 block 住,保證內容不會因為 Queue 滿了而遺失,但也可能會因此造成連鎖效應,如果發生異常 block 住的數量過多要如何快速地消耗,是否團隊有建立監控機制來預防此狀況發生,與上次討論提到的 cache 相似,團隊在選用前應該了解會遇到的問題及如何解決,才不會在上線後踩雷後無法處理造成業務上的損失。

    Kafka & RabbitMQ
    接著討論到常見的 Queue service 有 Kafka 及 RabbitMQ,分別是使用 Java 與 Erlang 不同的程式語言所開發,分別有各自程式語言的特性,像是 RabbitMQ 預設記憶體超過 40% 時會發出 memory 不足的警告,當 GC (Garbage Collection) 機制回收時會消耗兩倍記憶體量為 80% (50% used memory + 50% Crach memory,確保失敗的記憶體可以復原),這是 Erlang 程式語言的 safe to crash 的特性也是可能在使用 RabbitMQ 必須要注意的地方(缺點);接著談到 Kafka,Kafka 是 Linkedin 開發的 open source,Kafka 快的原因是 Java 有使用 off-heap,off-heap 是 GC 的一種技術,在 Java 有提供 unsafe 基礎類別 API 可以自己去控制記憶體或回收記憶體 (備註1),從效能鐵三角的角度來看 Kafka 是追求高 throughput。

    備註1 : 當 GC 啟動在執行時記憶體回收時,正在進行的執行緒會將暫停直到 GC 回收完畢為止,為了解決內存記憶體過大造成 GC 回收時間過長的問題, JAVA 提供 off-heap memory,將記憶體放在 heap 中將控制權交由作業系統處理,(感謝 Ant 大協助 Review & 糾正)

    心得
    過去曾經在面試中被問到 RDBMS 與 NoSQL 如何選擇,自己當時回答得七零八落,但經過這兩次小組的討論陸續有講到 RDBMS、NoSQL與 NewSQL 的本質設計,對於這問題的答案自己也開始有些頭緒。如同於程式架構的演進一樣,從 RDBMS 到 NoSQL 到最近的 NewSQL,各有適用的強項(解決的問題)與適用的場景,沒有一招打遍天下的解決方案。另外在討論架構時也提到不同解決方案的差異,也提醒同學必須思考什麼情況下會從一種模式轉為另一種架構模式,考量的點會是甚麼,舉例來說如果架構中某個服務loading 較高時,可以選擇抽出來提高其可用性與吞吐量,其餘沒有問題的研究既有架構繼續使用,像是積木一樣做組合與分配,選擇可以解決問題的方案才是真正的王道。

    參考
    Elasticache : 比較 Memcached 和 Redis 的差別
    Queue 的應用(4) - ManualResetEvent 與 Lock 的 BlockQueue 補完
    堆外內存(off-heap),堆內存(on-heap)
    Java魔法类:Unsafe应用解析
    Apache Kafka 介紹
    Blocking Queue
    RabbitMQ性能优化
    Difference between Row oriented and Column oriented data stores in DBMS
    Rowise vs Columnar Database? Theory and in Practice
    關聯式與NoSQL 資料
    Multi-Tenancy Application #2, 資料層的選擇
    Protocol Buffers
    MessagePack

    Copyright © m@rcus 學習筆記 | Powered by Blogger

    Design by Anders Noren | Blogger Theme by NewBloggerThemes.com