微服務(wù)之CRQS設(shè)計(jì)
CQRS是命令查詢職責(zé)隔離(Command Query Responsibility Segregation)的簡(jiǎn)稱(chēng)。其主要實(shí)現(xiàn)核心是通過(guò)使用事件來(lái)維護(hù)從多個(gè)服務(wù)復(fù)制數(shù)據(jù)的只讀視圖,借此實(shí)現(xiàn)對(duì)來(lái)自多個(gè)服務(wù)的數(shù)據(jù)查詢操作。CQRS將對(duì)持久化數(shù)據(jù)的使用劃分成兩個(gè)部分:命令端和查詢端。
命令端:負(fù)責(zé)數(shù)據(jù)的增刪改(CUD)操作
查詢端:負(fù)責(zé)數(shù)據(jù)的查詢(R)操作
這里需要說(shuō)明的是,CQRS不是簡(jiǎn)單的數(shù)據(jù)讀寫(xiě)分離,通常我們說(shuō)的讀寫(xiě)分離是指為了提升數(shù)據(jù)庫(kù)的效率,將數(shù)據(jù)庫(kù)分為讀庫(kù)和寫(xiě)庫(kù),寫(xiě)庫(kù)負(fù)責(zé)CUD操作,讀庫(kù)負(fù)責(zé)R操作,但是讀寫(xiě)分離有一個(gè)大前提,就是兩套數(shù)據(jù)庫(kù)里的數(shù)據(jù)是一致的(實(shí)時(shí)同步)。但是CQRS的查詢庫(kù)并不是和CUD主庫(kù)是一致的數(shù)據(jù),而是通過(guò)訂閱一些應(yīng)用事件,同步更新過(guò)來(lái)的有查詢需求的數(shù)據(jù)。甚至中間可能還存在一層數(shù)據(jù)加工的過(guò)程。

優(yōu)缺點(diǎn)
在許多方面,CQRS也代表了當(dāng)前流行的基于事件的數(shù)據(jù)庫(kù)應(yīng)用場(chǎng)景,例如它使用關(guān)系型數(shù)據(jù)庫(kù)作為記錄系統(tǒng),使用文本搜索引擎(如 Elasticsearch)來(lái)處理文本查詢。不同之處在于CQRS使用更廣泛的數(shù)據(jù)庫(kù)類(lèi)型,而不僅僅是文本搜索引擎。此外,通過(guò)訂閱事件,近乎實(shí)時(shí)的更新CQRS查詢端視圖。
優(yōu)點(diǎn)
在微服務(wù)架構(gòu)中高效的實(shí)現(xiàn)查詢(聚合了多數(shù)據(jù)源,統(tǒng)一了查詢視圖數(shù)據(jù))
高效地實(shí)現(xiàn)多種不同的查詢類(lèi)型(視圖數(shù)據(jù)庫(kù)可以是多種類(lèi)型的體現(xiàn))
在基于時(shí)間溯源技術(shù)的應(yīng)用程序中實(shí)現(xiàn)查詢。
更進(jìn)一步的實(shí)現(xiàn)了問(wèn)題隔離
缺點(diǎn)
更加復(fù)雜的架構(gòu):開(kāi)發(fā)人員必須編寫(xiě)更新和查詢視圖的查詢端服務(wù),管理和運(yùn)維額外的數(shù)據(jù)存儲(chǔ),提高了運(yùn)維成本。
處理數(shù)據(jù)復(fù)制導(dǎo)致的延遲:基于事件同步數(shù)據(jù)必然存在一定的延遲
設(shè)計(jì)CQRS視圖
通常一個(gè)CRQS視圖包括事件處理程序、查詢API、數(shù)據(jù)訪問(wèn)模塊和視圖數(shù)據(jù)庫(kù)四部分:
事件處理器:通過(guò)訂閱多個(gè)事件聚合更新視圖數(shù)據(jù)庫(kù)
查詢API:對(duì)外暴露的統(tǒng)一查詢接口
數(shù)據(jù)訪問(wèn)模塊:封裝對(duì)視圖數(shù)據(jù)庫(kù)的訪問(wèn)邏輯,包括DAO層轉(zhuǎn)換,數(shù)據(jù)冪等更新、并發(fā)處理等功能。
視圖數(shù)據(jù)庫(kù):根據(jù)CQRS實(shí)際業(yè)務(wù)需求,提供視圖數(shù)據(jù)查詢支撐的數(shù)據(jù)庫(kù)(SQL、NoSql)。

通常在開(kāi)發(fā)試圖模塊時(shí),我們要遵守一些通用的重要原則:
必須選擇適的低層數(shù)據(jù)庫(kù),并且設(shè)計(jì)數(shù)據(jù)庫(kù)結(jié)構(gòu)。
在設(shè)計(jì)數(shù)據(jù)訪問(wèn)模塊時(shí),必須解決各種數(shù)據(jù)訪問(wèn)問(wèn)題,包括確保更新是冪等的、能夠處理并發(fā)更新等常見(jiàn)問(wèn)題。
在現(xiàn)有應(yīng)用程序中實(shí)現(xiàn)新視圖或更改現(xiàn)有應(yīng)用程序的模式時(shí),必須實(shí)現(xiàn)一種機(jī)制,以便有效地構(gòu)建或重建視圖(即維護(hù)視圖的可擴(kuò)展性)。
必須決定如何設(shè)計(jì)視圖的客戶端,以應(yīng)對(duì)數(shù)據(jù)通過(guò)事件同步帶來(lái)的延遲問(wèn)題(即業(yè)務(wù)調(diào)用方要?jiǎng)”局卦嚮蛘呖山邮軘?shù)據(jù)延遲的問(wèn)題),建議客戶端應(yīng)用程序采用最終一致性的視圖。
關(guān)鍵的設(shè)計(jì)決策還是在數(shù)據(jù)庫(kù)的選擇和數(shù)據(jù)庫(kù)結(jié)構(gòu)的設(shè)計(jì)上面,數(shù)據(jù)庫(kù)和數(shù)據(jù)庫(kù)模型的存在主要的目的是有效地實(shí)現(xiàn)視圖模塊的查詢操作。在一些場(chǎng)景下,NoSQL數(shù)據(jù)庫(kù)比SQL數(shù)據(jù)庫(kù)更有優(yōu)勢(shì),主要體現(xiàn)在靈活的數(shù)據(jù)模型以及更好的性能和可擴(kuò)展性方面。但有時(shí)選擇SQL數(shù)據(jù)庫(kù)也是有意義的,可以根據(jù)各團(tuán)隊(duì)的技術(shù)棧水平,運(yùn)維能力等多方面因素來(lái)衡量,MySQL針對(duì)Json格式的支持讓其突破了傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)的數(shù)據(jù)結(jié)構(gòu)關(guān)聯(lián)瓶頸,也可以滿足我們?nèi)粘5囊恍┎樵兡P托枨蟆?/p>
使用場(chǎng)景 | 建議選型 | 舉例說(shuō)明 |
基于主鍵的JSON對(duì)象查詢 | 文檔型數(shù)據(jù)庫(kù)。例如MongoDB或DynamoDB,或者使用Redis這樣的鍵值存儲(chǔ)數(shù)據(jù)庫(kù)。 | 根據(jù)登錄賬號(hào)獲取博文信息。 |
基于查詢的JSON對(duì)象查找 | 文檔型數(shù)據(jù)庫(kù)。例如MongoDB或DynamoDB | 根據(jù)樓主論壇,查詢?cè)u(píng)論信息。 |
文本查詢 | 文本搜索引擎,例如Elasticsearch。 | 根據(jù)訂單編號(hào)查詢訂單詳情的文本搜索或訂單交易日志詳情。 |
圖查詢 | 圖數(shù)據(jù)庫(kù),例如Neo4j。 | 通過(guò)維護(hù)客戶、訂單和其他數(shù)據(jù)的圖表來(lái)實(shí)現(xiàn)欺詐檢查。 |
傳統(tǒng)的SQL報(bào)表/BI | 關(guān)系型數(shù)據(jù)庫(kù),例如MySQL、Oracle、SQLServer等。 | 標(biāo)準(zhǔn)業(yè)務(wù)報(bào)表和分析 |
