有時候,客戶需要一個資料庫來作為「查詢」專用資料庫,因為他需要查詢大量的資料,或是在既有系統中已經將相關的資料分享給某些整合資料庫的工具集使用(例如:Tableau 工具等,經常是用來做業務分析相關業務的資料庫)。
在這個情境下,允許其他的模組來存取這些「線上分析資料庫」內的資料相對就是有意義的,但要注意的是應該將「服務邊界內使用的資料庫」與「外部公開的資料庫分開 (寫入服務邊界內的資料庫,同步到「線上分析資料庫」)。
具體的實踐方式,就是建立專用的「唯讀資料庫」讓其他的服務元件透過這個端點來存取資料做「大量的資料分析作業」,特別適合用來做「批次報表等相關的業務」。
這個技巧,並不是在「微服務」中獨有的操作。在很多年前,那是一個還在討論服務導向架構的時代,那時候我有一段時間在研究 IBM 的 Business Process Management 解決方案,在該方案中有包含「業務流程」應用的資料庫以及「監控分析」應用的資料庫,其概念就像上圖一般,將「交易」資料庫與「分析」資料庫依據需求分開使用。
Martin Fowler 有一篇部落格文章提到「回報資料庫模式」,該文章提到多數企業級應用(Enterprise Applications)會使用資料庫來儲存持久化資料。
這個資料庫同時支援應用程式營運用的狀態更新,以及用於決策支援與分析的各式報表。很多時候,我們會在應用系統中看到這兩個類型的資料被存在同一個資料庫,但沒想過從技術上其實這兩類型的「需求」其實不太一樣。
所以,比較好的做法會是將「交易資料庫」與「報表資料庫」做分離,報表資料庫的來源是「交易資料庫」,但它會轉換 Schema 讓報表的查詢更佳有效率,甚至這兩個資料庫可以是完全不同的資料庫,例如:報表資料庫可以採用 NoSQL 相關的資料庫。
獨立報表資料庫的優缺點:
優點:
缺點:
報表資料庫的缺點是需要維持資料為最新。最簡單的情境是透過夜間批次將資料匯入報表資料庫 (這是業界常見的做法)。這通常效果不錯,因為許多報表用前一天的資料即可。
但是,在我們的案例中希望這個部分可以更加「即時」,畢竟其他服務可能不是真的「報表」,而是要透過事件的方式做到近即時的狀態,所以我們在上述方案中引用了資料庫中 CDC (異動資料截取) 解決方案,讓資料更新得更即時。
所以,報表資料庫並非即時一致的資料,而是符合微服務精神的「最終一致機制」。Sam Newman 在「單體式系統到微服務」一書中將這個模式稱之為「資料庫服務介面」。
在微服務架構中討論「讀寫分離」,是因為讀取與寫入在負載型態、擴展策略、資料一致性與風險隔離上有本質差異。
將寫入集中在擁有者服務的主資料庫(或主資料模型)以確保正確性,再用 Read Replica/快取承接處理高併發查詢,可把擴展重點放在讀(但是,系統複雜度是出現在「寫」,分析與設計的重點在寫)、降低主資料庫的壓力,並把查詢失敗風險與昂貴授權成本隔離。
此外,讀寫分離常與 CQRS、Outbox/CDC 事件同步搭配:寫入端先正確落帳並產生事件,讀端以非同步方式更新去正規化的查詢模型,既避免跨服務/跨資料庫分散式交易,又兼顧效能與可用性。
讀寫分離是微服務在效能、可用性、成本與一致性之間取得務實平衡的關鍵手段。
把「交易」與「查詢」拆開,不是為了追最新的技術樣式,而是為了讓系統在效能、可用性、成本與一致性之間取得最務實的平衡。
以唯讀的線上分析/報表資料庫 承接大量讀取的流量,讓主資料庫專注正確寫入;再以 CDC/Outbox 事件 將變更同步到讀取端,配合 CQRS/去正規化讀模型,我們既避免跨庫分散式交易的代價,也保留「最終一致」下可預期的延遲。
關鍵在於清楚的服務邊界(所以才會讓微服務與 Domain Driven Design 這麼緊密)與資料擁有權:寫入只發生在領域內,對外提供穩定的讀取介面,必要時才開放報表資料庫。
別把報表資料庫當成改善的最後終點,它只是負責分擔過載流量與支撐分析的專用工具;真正的成熟,是能按情境選擇批次作業到近即時同步 (event streaming) 的節奏,並以觀測性與變更治理將風險收斂。
當我們漸進地完成這套讀寫分離策略,微服務的價值才會穩穩地體現在你的系統與組織裡。