在一般的**的架構中,我們都會採用快取架構來抗住高併發場景下的讀請求。那麼對於寫請求,先更新快取還是先更新資料庫?
本文以商品庫存資訊為例,我們展開討論,假設剛開始資料庫庫存=100,快取中庫存=100.
1.先更新資料庫,後更新快取
這種情況下,當需要更新庫存的時候,先更新資料庫中的庫存=99,然後再更新快取=99。
但是想象一種場景,由於網路原因,資料庫更新成功,但是快取更新失敗,那麼此時來了讀請求,那麼此時會讀到快取中的資料,與資料庫中的資料不一致,那麼就會產生錯誤。
2. 先更新快取,後更新資料庫。
使用該策略,當需要更新庫存的時候,先更新快取中庫存=99,然後再更新資料庫=99。
此時假設更新完快取之後,由於網路原因,資料庫還沒有更新成功,但是此時有乙個寫請求過來,去快取中請求資料得到的是更新之後的資料,然而資料庫中還是之前的資料,這樣就產生了不一致性的問題。雖然這種不一致性情況有些牽強,有人認為,寫請求在前,讀請求在後,雖然資料庫此時沒有更新成功,但是一旦網路恢復就會更新成功,與之前讀到的是一致的,那麼我們暫且認為這種情況也可以行得通。
那麼來看看該方案下的另一種情況,如果此時有大量的寫請求,每次寫請求都需要更新快取,然後更新資料庫,假設10ms中有10個寫請求,但是只有乙個讀請求,需要更新10次快取和10次資料庫。這樣在高併發場景下會給系統帶來很大的延遲。那麼,這種方案真的可行嗎?真的是更新嗎?
3.先刪除快取,後更新資料庫
這裡要強調刪除,而不是更新,我們可以看到上面一種情況在高併發場景下根本不適用,先刪除快取,之前需要更新10次快取,10次資料庫,我們優化為只需要刪除一次快取,更新10次資料庫,當有乙個讀請求過來只是沒有命中,去資料庫讀取一下,這樣對效能的影響也不是很大。
那麼,再次思考一下,這樣做在高併發情況下真的可以保證資料庫和快取中資料一致嗎?
在高併發場景下,當乙個寫請求過來之後,我們刪除了快取,還沒有更新資料庫,緊接著來了乙個讀請求,發現快取中的資料被刪除了,去資料庫中讀取資料後返回給使用者,同時也更新到快取。在這一系列請求結束之後,寫請求才將資料庫的資料更新到最新值,此時資料不一致性的問題。
二、高併發場景下的優化方案
在上面,我們主要發現最主要的問題是資料庫沒有更新成功,讀請求就直接讀資料庫了。那麼我們是否可以讓讀請求在寫請求完成之後再去讀資料庫呢?
1.訊息佇列序列化方案
該方案的主要思路是在後台程序中我們可以建立多個佇列,然後根據hash演算法將寫請求路由到不同的佇列中,當來讀請求的時候,就加入佇列中,當寫請求處理完畢後,再去處理讀請求。
該方案需要注意以下幾點:
可能會有大量讀請求,這樣讀請求不就序列化了?因此只需要將第乙個讀請求加入佇列中,其他讀請求阻塞在cache。
需要將同乙份資料的寫請求和讀請求路由到同乙個佇列中。
這種情況下,如果對於同乙份資料有多個寫請求同時在佇列中,那麼來乙個讀請求中加入佇列中之後,一般寫請求耗時比較久,那麼讀請求會需要很久才能返回。也許是快取中根本沒有,我們只需直接去資料庫中讀取即可,但是加入佇列中卻需要等待這麼長時間。
因此,我們可以設定讀請求在佇列中的等待超時時間,當達到一定時間時還在等待,那麼直接去資料庫讀取返回即可。
高併發快取 資料庫雙寫不一致
情景一 先修改資料庫,再刪除快取,如果刪除快取失敗了,那麼會導致資料庫中是新資料,快取中是舊資料,資料出現不一致 解決方案 先刪除快取,再修改資料庫,如果刪除快取成功了,如果修改資料庫失敗了,那麼資料庫中是舊資料,快取中是空的,那麼資料不會不一致 因為讀的時候快取沒有,則讀資料庫中舊資料,然後更新到...
快取不一致
當程式在執行過程中,會將運算需要的資料從主存複製乙份到cpu的快取記憶體當中,那麼cpu進行計算時就可以直接從它的快取記憶體讀取資料和向其中寫入資料,當運算結束之後,再將快取記憶體中的資料重新整理到主存當中。舉個簡單的例子 i i 1。當執行緒執行這個語句時,會先從主存當中讀取i的值,然後複製乙份到...
快取與資料庫不一致的問題
一 資料庫主從不一致 先回顧下,無快取時,資料庫主從不一致問題。如上圖,發生的場景是,寫後立刻讀 1 主庫乙個寫請求 主從沒同步完成 2 從庫接著乙個讀請求,讀到了舊資料 3 最後,主從同步完成 導致的結果是 主動同步完成之前,會讀取到舊資料。可以看到,主從不一致的影響時間很短,在主從同步完成後,就...