如何保證數據庫與緩存雙寫時的數據一致性



最經典的緩存+數據庫讀寫的模式:cache aside pattern

Cache Aside Pattern

  讀的時候,先讀緩存,緩存沒有的話,那么就讀數據庫,然后取出數據后放入緩存,同時返回響應

  更新的時候,先刪除緩存,然后再更新數據庫   (很多地方都說應該先更新數據庫,再刪緩存)

 

為什么是刪除緩存,而不是更新緩存呢?

  原因很簡單,很多時候,復雜點的緩存的場景,因為緩存有的時候,不簡單是數據庫中直接取出來的值

  比如可能更新了某個表的一個字段,然后其對應的緩存,是需要查詢另外兩個表的數據,並進行運算,才能計算出緩存最新的值的

  更新緩存的代價是很高的

  如果你頻繁修改一個緩存涉及的多個表,那么這個緩存會被頻繁的更新

  但是問題在於,這個緩存到底會不會被頻繁訪問到?

  舉個例子,一個緩存涉及的表的字段,在1分鍾內就修改了20次,或者是100次,那么緩存更新20次,100次; 但是這個緩存在1分鍾內就被讀取了1次,有大量的冷數據

  實際上,如果你只是刪除緩存的話,那么1分鍾內,這個緩存不過就重新計算一次而已,開銷大幅度降低

  每次數據過來,就只是刪除緩存,然后修改數據庫,如果這個緩存,在1分鍾內只是被訪問了1次,那么只有那1次,緩存是要被重新計算的,用緩存才去算緩存

  其實刪除緩存,而不是更新緩存,就是一個lazy計算的思想,不要每次都重新做復雜的計算,不管它會不會用到,而是讓它到需要被使用的時候再重新計算

 


最初級的緩存不一致問題以及解決方案

  問題:先修改數據庫,再刪除緩存,如果刪除緩存失敗了,那么會導致數據庫中是新數據,緩存中是舊數據,數據出現不一致

  解決思路:先刪除緩存,再修改數據庫,如果刪除緩存成功,修改數據庫失敗,那么數據庫中是舊數據,緩存中是空的,那么數據不會不一致

  因為讀的時候緩存沒有,則讀數據庫中舊數據,然后更新到緩存中

 

比較復雜的數據不一致問題分析

  數據發生了變更,先刪除了緩存,然后要去修改數據庫,此時還沒修改

  一個請求過來,去讀緩存,發現緩存空了,去查詢數據庫,查到了修改前的舊數據,放到了緩存中

  數據變更的程序完成了數據庫的修改,此時緩存是舊數據,數據庫是新數據

 

解決方案:更新與讀取操作進行異步串行化

  更新數據的時候,根據數據的唯一標識,將操作路由之后,發送到一個jvm內部的隊列中

  讀取數據的時候,如果發現數據不在緩存中,那么將重新讀取數據+更新緩存的操作,根據唯一標識路由之后,也發送同一個jvm內部的隊列中

  一個隊列對應一個工作線程

  每個工作線程串行拿到對應的操作,然后一條一條的執行

  這樣的話,一個數據變更的操作,先執行,刪除緩存,然后再去更新數據庫,但是還沒完成更新

  此時如果一個讀請求過來,讀到了空的緩存,那么可以先將緩存更新的請求發送到隊列中,此時會在隊列中積壓,然后同步等待緩存更新完成

 

  這里有一個優化點,一個隊列中,其實多個更新緩存請求串在一起是沒意義的,因此可以做過濾,如果發現隊列中已經有一個更新緩存的請求了,那么就不用再放個更新請求操作進去了,直接等待前面的更新操作請求完成即可

  待那個隊列對應的工作線程完成了上一個操作的數據庫的修改之后,才會去執行下一個操作,也就是緩存更新的操作,此時會從數據庫中讀取最新的值,然后寫入緩存中

  如果請求還在等待時間范圍內,不斷輪詢發現可以取到值了,那么就直接返回; 如果請求等待的時間超過一定時長,那么這一次直接從數據庫中讀取當前的舊值

 

 

轉自:中華石杉Java工程師面試突擊

 

 

 


免責聲明!

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



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