從數據庫讀寫分離到CQRS
1. 數據庫讀寫分離
對于數據庫的操作就四種:CRUD
我們把這四種操作,又劃分為兩類,讀和寫

當我們的系統并發量高的時候,自然會考慮到提高數據庫性能,數據庫讀寫分離,

但是,實際測試下來,總是有各種不滿意的地方。其中最麻煩的就是各種復雜查詢的性能,寫庫有單點故障問題
2. CQRS
有了數據庫層面的”歸納“:CRUD->讀、寫, 可以繼續”歸納“:CRUD->讀、寫 --> 查詢、命令
將查詢和命令分開,也就是我們要說的CQRS(Command Query Responsibility Segregation)模型,命令與查詢職責分離
這樣,我們在應用層面就可以抽象成如下模型:

乍一看,這套東西其實和采用的數據庫的讀寫分離是一樣的,就是把讀寫給分開,但是這些并沒有那么簡單
其實是模型的不同,原來的數據庫讀寫分離確實把讀寫的這兩個行為分開了,但是它依然有一個重要的事情沒有做,那就是職責的分開

什么叫職責的分開呢?就是讀寫雙方不要搞同一套模型。而數據庫讀寫分離的問題就在這里,它使用了同一個模型。
使用同一個模型在這里造成的問題是,這個模型由于既要考慮讀取數據不能太困難,也要考慮寫入數據不能太困難。
使用 CQRS 思想的話,寫入不需要關心讀取的問題,讀取數據也不用關心寫入的問題,那就可以做到真正的讀寫分離,提高性能
寫存儲可以用MySQL這樣的關系型數據庫,而查詢存儲則可以使用Elasticsearch作為存儲。命令-查詢職責分離(CQRS)模式是一種應用于這種場景的通用模型,它顯式地將系統中的讀(查詢)和寫(命令)進行分離
優點:
1. 拆分了這兩塊的代碼,使各自可以采用不同的技術棧,做針對性的調優。命令模型負責數據的變更,并把最新數據同步給查詢模型。
2. 切分了流量,能夠更靈活的做資源分配,處理數據邏輯的時候,查詢模型根據自己的想法來安排數據,想怎么用就怎么用
缺點:
1. 引入 CQRS 的模式后,最大的問題在于引入了過度的復雜性, 由于需要讀和寫分開,那么我們開發的工作量無形中被加大了一倍。又引入 CQRS,這變得更復雜了, 查詢想要更好的性能可能就得考慮開源的搜索引擎中間件。每引入一種都會增加開發成本、服務器成本,以及更多的復雜度
2. 最終一致性:如果分離讀取和寫入數據庫,讀取數據可能會過時
a. 如果我們采用了CQRS模式,但是命令和查詢兩側底層所依賴的數據模型并未分離,而是基于共享的數據存儲和數據模型,命令和查詢之間不需要額外的交互,命令側的數據更新對查詢側實時可見。在這種架構模式下,兩側基于共享的數據已經天然的集成在一起,不需要額外機制進行通信,自然也無需引入消息了。
b. 如果我們采用CQRS模式,并且命令和查詢兩側進行了數據模型的分離,二者各自依賴獨立的數據模型。同時,數據存儲也分開部署。命令側負責數據的更新,而查詢側只負責數據的查詢,如何將數據的更新及時同步到查詢側是需要解決的問題。在這種架構模式下,使用消息模式作為兩側的通信機制是個不錯的選擇
3 種主要的 CQRS 架構
1. 單數據庫 CQRS
顧名思義就是command和query都是操作的同一個數據庫

2. 雙數據庫 CQRS
在“雙數據庫”方式中,我們需要兩個數據庫,一個用于寫操作,一個用于讀操作。命令端使用針對寫操作優化的數據庫。查詢端使用針對讀取操作優化的數據庫

3. 事件源 (EventSourcing) CQRS
最復雜的 CQRS 架構。與前面兩種方式相比,事件源存儲數據的思路完全不同。在事件源方法中,我們并不只存儲實體的當前狀態,而且將實體發生的每一個狀態作為快照來存儲。實體并不是以標準化數據的形式保存,而是通過事件的時間戳來保存它們的變更。
(可以參考Mysql的Binlog設計)這種記錄的優點是可以根據回放,重現每一次狀態變更的時間點以及變更軌跡。而查詢則可以根據當前狀態的快照來為查詢提速
Event Sourcing也叫事件溯源,是Martin Fowler提出的一種架構模式。其設計思想是系統中的業務都由事件驅動來完成。系統中記錄的是一個個事件,由這些事件體現信息的狀態。業務數據可以是事件產生的視圖,不一定要保存到數據庫中
為了便于理解Event Sourcing 我們通過一個例子來進一步解釋:
1. 創建了一個銀行賬戶,假設此時的賬戶ID為“0001”。
2. 針對“0001”這個賬戶存入300元現金。
3. 然后從“0001”這個賬戶取出100元現金。
4. 最后,再存入200元。

上面生成的這一系列事件會保存到下方的Event Store的事件庫中,這里并不會保存“賬戶”的狀態信息。當需要獲取“賬戶”數據的時候,會通過這些事件信息,還原成“賬戶”的最終狀態,也就是“賬戶ID”為“0001”,“賬戶金額”為400。其具體實現方式是,通過賬戶相關的四個事件對應的處理方法,重新生成當前狀態。如果每次查詢狀態信息都需要這樣處理勢必會造成資源的浪費,因此在右側黃色的部分,我們將最終的“賬戶”信息通過視圖的方式保存下來,以供查詢
上面這個“賬戶”處理的過程,就是Event Sourcing,說白了就是通過事件的處理模式。它將系統中的操作都按照事件的方式記錄并保存,任何實體的最終狀態都是通過事件的疊加和還原確認的
從CQRS模式的結構看
實體狀態的變化發生在Command端,Command端知道業務處理進行了哪些具體操作,將這些具體的操作進行封裝就形成了Event。
而Query端,查詢返回的是實體當前狀態狀態。根據“當前狀態 + 變化 = 新的狀態”,如果能從Command端得到“變化”,再加上Query端自身獲取的“當前狀態”就能得到變化后的“新的狀態”
此時Command 端發出的Event正好符合這個“變化”,如果當變化發生也就是新Event產生時,由Command端將這個Event通過EventHandler將這個信息存放到Reader Database(也可以理解為視圖)中,這樣新的Event 信息加上當前的實體信息就時最新的實體信息了,Query端根據Event刷新狀態,就能保證兩端實體狀態一致,達到最終一致性


浙公網安備 33010602011771號