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

引言

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

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

博主當時以爲,這種更新策略比較簡單,不必多作說明,結果太多人留言給博主,問我爲何不說這套方案?好吧,博主先跟你們道個歉,是個人問題。因此再開一文,把這個方案說明一下web

正文

下面說明一下先更緩存,再更新數據庫這套方案
更新數據庫失敗了怎麼辦?
這個問題其實很好解決,提供一個補償措施便可。這個補償措施,你們靈活變通,博主只是舉例,以下圖所示:
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年》這篇博客,
博主偷個懶,直接貼一下該博客的原話sql

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

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

因而商品表裏面增長了這樣一個字段,每增長一個PV這個字段就要更新一次

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

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是有序的,就會出現最上面說的線程安全問題。因此,但願你們針對問題多思考總結。
給你們留一個思考問題?
若是此時是一個讀多寫多的場景,又要求更新數據庫的操做必須嚴格保證順序,那這個時候怎麼保證緩存和數據庫的一致性?你們能夠來個人博客留言。網絡

總結

本文是對上次文章的一次文章的一次補充。只怪博主思考問題太過簡單,給你們留了個坑。所以再開一篇文章進行補充說明。但願你們可以有所收穫。併發

相關文章
相關標籤/搜索