【原創】分布式之數據庫和緩存雙寫一致性方案解析(二)


引言

該文是對《分布式之數據庫和緩存雙寫一致性方案解析》,一文的補充。博主在該文中,提到了這么一句話

應該沒人問我,為什么沒有先更新緩存,再更新數據庫這種策略。

博主當時覺得,這種更新策略比較簡單,沒必要多做說明,結果太多人留言給博主,問我為什么不說這套方案?好吧,博主先跟大家道個歉,是我的問題。所以再開一文,把這個方案說明一下

正文

下面說明一下先更緩存,再更新數據庫這套方案
更新數據庫失敗了怎么辦?
這個問題其實很好解決,提供一個補償措施即可。這個補償措施,大家靈活變通,博主只是舉例,如下圖所示:
image
流程如下所示
(1)更新緩存數據;
(2)更新數據庫失敗
(3)將需要更新的sql發送至消息隊列
(4)自己消費消息,獲得需要更新的sql
(5)繼續重試更新操作,直到成功
其他方案不列舉,因為重點不在這,在下面的情況
有存在其他的線程安全問題么?
有的,假設這會同時有請求A和請求B進行更新操作,那么會出現
(1)線程A更新了緩存
(2)線程B更新了緩存
(3)線程B更新了數據庫
(4)線程A更新了數據庫
請求A更新數據庫應該比請求B更新數據庫早才對,但是因為網絡等原因,B卻比A更早更新了數據庫。這就導致了臟數據,因此不考慮。
可是,這時候有一個細心的讀者,給博主舉了一個反例。該例子出自《從P1到P7——我在淘寶這7年》這篇博客,
博主偷個懶,直接貼一下該博客的原話

在【招財進寶】項目中有一個技術的細節值得拿出來說說,淘寶商品詳情頁面每天的流量在10億以上,里面的內容都是放在緩存里的,做【招財進寶】的時候,我們要給賣家顯示他的商品被瀏覽的次數,這個數字必須實時更新,而用緩存的話一般都是異步更新的。於是商品表里面增加了這樣一個字段,每增加一個PV這個字段就要更新一次。發布上去一個小時數據庫就掛掉了,撐不住這么高的update。數據庫撐不住怎么辦?一般的緩存策略是不支持實時更新的,這時候多隆大神想了個辦法,在apache上面寫了一個模塊,這個數字根本不經過下層的web容器(只經過apache)就寫入一個集中式的緩存區了,這個緩存區的數據再異步更新到數據庫。好像什么問題,到了多隆手里,總能迎刃而解。

好吧,如果沒耐心的讀者,直接看博主的總結吧。上面巴拉巴拉一堆,就是說,當時他們有一個讀多寫多的場景,然后多隆大神用了先更緩存,再異步更新數據庫的策略。
難道淘寶的大神沒發現線程安全問題?
不是的,上面提到的場景具有一個特殊性。我們先摘取關鍵一句話

於是商品表里面增加了這樣一個字段,每增加一個PV這個字段就要更新一次

ps:PV是page view,頁面瀏覽量的意思。
博主斗膽猜測,他們做的應該是用戶每次點擊,數據庫里的這個字段就加一的操作。
那我們這時的SQL一般是這么寫

update product_tb set number = number+1 where product_id =xxx

大家注意到了么,並發執行這句SQL並不需要關心執行順序。哪個更新線程先執行加一的SQL語句 ,與操作順序有什么關系呢?
再說的通俗一點,假設我們同時有請求A和請求B進行更新操作,那么會出現
(1)線程A更新了緩存
(2)線程B更新了緩存
(3)線程B更新了數據庫
(4)線程A更新了數據庫
因為他們這個時候執行的sql是無序的,所以上面的步驟(3)和步驟(4)哪一個步驟先執行,並沒有關系。最終結果一定是一致的。
容博主啰嗦,來個實例,假設表product_tb如下

product_id number
1 3

這時請求A和請求B同時對product_id為1的數據進行更新操作,無論是按出現並發問題時的順序
(1)線程B更新了數據庫,進行加一
(2)線程A更新了數據庫,進行加一
還是正常的順序
(1)線程A更新了數據庫,進行加一
(2)線程B更新了數據庫,進行加一
最終結果都是

product_id number
1 5

ok。說到這里,大家應該是懂了。換句話說,如果此時,操作的sql是有序的,就會出現最上面說的線程安全問題。所以,希望大家針對問題多思考總結。
給大家留一個思考問題?
如果此時是一個讀多寫多的場景,又要求更新數據庫的操作必須嚴格保證順序,那這個時候怎么保證緩存和數據庫的一致性?大家可以來我的博客留言。

總結

本文是對上次文章的一次文章的一次補充。只怪博主思考問題太過簡單,給大家留了個坑。因此再開一篇文章進行補充說明。希望大家能夠有所收獲。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM