資料變更通知的一種方案

weixin_34378969發表於2018-01-15

問題提出

我最近發現了一個很有意思的問題。常見的業務解決都是拉模型。如,當使用者查詢其訂單,其流程是經過web層,服務層,到資料庫,按照層級關係進行組織。上一層主動拉下一層的資料,下一層除非有人拉資料,否則不會執行任何的動作。

2579123-d28803f41921df5b.png
image.png

這在某些情況下會出現一些問題。比如說有快取的情況下。常見的即,在同一個服務內,不同的介面修改了底層的資料,但是卻沒有重新整理快取。

2579123-8f48ffe1ecea272a.png
image.png

當然,這在我們使用快取的時候就預計到了會出現這種情況。多數情況下,也不會有什麼問題。但是,如果一個業務需求既要求高效能——即要求QPS高,又要求響應時間短,同時還要求資料實時性好,那麼這種常見的模式,很難滿足了。我舉一個例子來說,商家設定的有關價格的資訊,是希望能夠在客戶端得到反饋的,而使用者在客戶端上訪問量是極高的。這就是一個典型的場景。

通常對效能苛刻意味著需要進行快取,而對資料實時性的要求,又是不好進行快取的。它並非是不能快取,可以通過設定快取過期時間很短,以達到準實時性——如果業務允許的話。且不談這個準實時是否能達到業務需求,單單一個設定短的快取過期時間,就會帶來快取頻繁重新整理的問題。在資料不經常變化,並且高併發的情況下,短的過期時間,會給伺服器帶來巨大的壓力,因為快取過期還容易導致快取穿透的問題。尤其是,如果資料不經常變動,那麼設定一個短的過期時間,幾乎毫無意義。

解決方案

當然,現在也有一種比較成熟的方案,只是沒有見到成體系的文字描述,現在我記錄於下。我稱之為“預計算模型”。它強調的是,在資料變更途徑可控的情況下,每次資料變更之後,重新計算各色業務。

這裡要區別兩個角色,一個是資料變更方,另外一個是業務方。使用到這份資料的業務方會有很多,但並不是所有的業務方都有這種苛刻的需求。所以實際上,這裡的業務方僅僅指那些關心資料變動的業務方。資料變更方在資料變更之後需要通知業務方,那些關心這些變動的業務方,則立刻執行重新計算的邏輯。

這裡額外討論一下通知的方式。通知的方式有多種。不論採用何種方式,其核心在於,資料變更方不應該知道有誰關心這個變更。如果變更方必須知道要有幾個業務方,以至於每次接入一個新的業務方的時候,都需要變更方更改自身的邏輯,那麼這就是典型的反向耦合了。我有兩個比較好的建議,第一個建議是採用觀察者模式。這種模式要求觀察者能夠自動註冊到被觀察者(也就是業務變更方)處,如果需要被觀察者主動尋找觀察者,那麼就陷入了反向耦合;另外一個建議是採用訊息通知機制。

該模型最大的困難在於,很多時候無法控制所有的變更入口。更可怕的是,在當下控制住了入口,結果一段時間後,出現了一個新入口,而你卻毫不知情。這很顯然會出現資料不一致的情況。一種顯而易見的緩解方法——它並不能徹底解決這個問題——就是,設定一個快取過期的時間,或者在間隔一段時間之後執行一些修復操作,由程式檢測到資料不一致的地方,並且將其同步到一致的狀態。

避免出現這樣情況的一種方法是,在資料持久化入口——通常也就是資料庫訪問層面做控制。舉一個例子,使用spring的aop技術,在所有呼叫裡面新增上通知機制的程式碼。這難免會帶來效能的損耗。因此,可以提供非同步通知機制。

實際上,在當下微服務盛行的時代,如果微服務劃分得好的話,這些入口是很好收攏的。因為這些入口必然是位於微服務的邊界之處,只要控制好了這幾個地方,就等於控制住了別的地方。我反對的一種常見的錯誤實踐,就是因為好幾個微服務都由一個團隊維護,因此他們會無意間打破這種邊界,比如說A服務訪問B服務的資料庫,而不是通過B服務提供的服務來訪問(不用懷疑,我吐槽的就是我們團隊,233333)。

後記

這個模型實際上會有一些違背直覺的感覺。其實不然。如果從分層結構上說,這種舉措,相當於底層主動通知上層。這是一種很典型的分層結構下的變更通知機制,有時候也被稱為回撥機制。它和一般的“底層呼叫上層”的區別就在於,它並沒有直接呼叫上層,而僅僅是通知了上層。從這個意義上來說,使用觀察者模式並不是一個很好的方案,採用訊息機制,其耦合程度要更加弱。

相關文章