知識庫
現在將高速緩存應用于業務當中已經十分常見了,甚至可能跟數據庫的頻率不相上下。你的用戶量如果上去了,直接將一個裸的 MySQL 去扛住所有壓力明顯是不合理的。這里的高速緩存,目前業界主流的就是 Redis 了,如何保證緩存與數據庫的雙寫一致性?
服務器高并發解決方案:http://www.bxgb88.com/dedicated/hkcdn.html
你只要用緩存,就可能會涉及到緩存與數據庫雙存儲雙寫,你只要是雙寫,就一定會有數據一致性的問題,那么你如何解決一致性問題?
上億高并發,如何保證緩存與數據庫的雙寫一致性?
1、最初級的緩存不一致問題以及解決方案
問題:先修改數據庫,再刪除緩存,如果刪除緩存失敗了,那么會導致數據庫中是新數據,緩存中是舊數據,數據出現不一致。
解決思路:
先刪除緩存,再修改數據庫,如果刪除緩存成功了修改數據庫失敗了,那么數據庫中是舊數據,緩存中是空的,那么數據不會不一致,因為讀的時候緩存沒有,則讀數據庫中舊數據,然后更新到緩存中。
2、并發下數據緩存不一致問題分析
問題:
第一個請求數據發生變更,先刪除了緩存,然后要去修改數據庫,此時還沒來得及去修改;
第二個請求過來去讀緩存,發現緩存空了,去查詢數據庫,查到了修改前的舊數據,放到了緩存中;
第三個請求讀取緩存中的數據 (此時第一個請求已經完成了數據庫修改的操作)。
完了,數據庫和緩存中的數據不一樣了。。。。
問題分析:
只有在對同一條數據并發讀寫的時候,才可能會出現這種問題。其實如果說你的并發量很低的話,特別是讀并發很低,每天訪問量就1萬次,那么很少的情況下,會出現剛才描述的那種不一致的場景;但如果每天的是上億的流量,每秒并發讀是幾萬,每秒只要有數據更新的請求,就可能會出現上述的數據庫+緩存不一致的情況。
解決思路:
數據庫的緩存更新與讀取操作進行串行化,一個隊列對應一個工作線程,每個工作線程串行拿到對應的操作,然后一條一條的執行。
1. 首先我們的項目里維護一組線程池和內存隊列。
2. 更新數據的時候,根據數據的唯一標識將請求路由到一個jvm隊列中,去更新數據庫,然后請求結束。
3. 讀取數據的時候,先查緩存,如果發現數據不在緩存中,那么將根據唯一標識路由之后,也發送同一個jvm內部的隊列中,重新讀取數據庫后更新緩存,最后請求結束。
這里有一個需要優化的點,比如一個隊列中,連續存在多個更新緩存請求串在一起是沒意義的,這樣重復的查詢數據庫并更新緩存的操作應該優化:如果發現隊列中已經有一個更新緩存的請求了,那么就不用再放個更新請求操作進去了,直接讓后面的讀請求阻塞個200ms左右(這里只是舉個例子,實際值可以根據服務的響應時間和機器的處理能力來計算),然后再次查詢緩存,如果緩存沒有值就查數據庫,拿到結果后不用更新緩存,直接返回給頁面即可。