如何保證Redis與數(shù)據(jù)庫的數(shù)據(jù)一致性(IT楓斗者)
一,起因
在高并發(fā)的業(yè)務(wù)場景下,大多數(shù)情況數(shù)據(jù)庫都是用戶并發(fā)訪問最薄弱的環(huán)節(jié)。因此,redis就充當了一個緩存手段,讓請求先訪問到redis,而不是直接訪問MySQL等數(shù)據(jù)庫。

遇到這個業(yè)務(wù)場景,主要是解決讀數(shù)據(jù)從Redis緩存,一般都按照下圖的流程來進行業(yè)務(wù)操作的:

二,解決方案
1,采用延時雙刪策略
在寫庫前后都進行redis.del(key)操作,并且設(shè)定合理的超時時間。
偽代碼如下:

具體的操作步驟:
先刪除緩存;
再寫數(shù)據(jù)庫;
休眠500毫秒;
再次刪除緩存。
那么,這個500毫秒怎么確定的,具體該休眠多久呢?需要評估自己的項目的讀數(shù)據(jù)業(yè)務(wù)邏輯的耗時。這么做的目的,就是確保讀請求結(jié)束,寫請求可以刪除讀請求造成的緩存臟數(shù)據(jù)。當然這種策略還要考慮redis和數(shù)據(jù)庫主從同步的耗時。最后得寫數(shù)據(jù)的休眠時間:則在讀數(shù)據(jù)業(yè)務(wù)邏輯的耗時基礎(chǔ)上,加幾百ms即可。比如:休眠1秒。
設(shè)置緩存過期時間
從理論上來說,給緩存設(shè)置過期時間,是保證最終一致性的解決方案。所有的寫操作以數(shù)據(jù)庫為準,只要到達緩存過期時間,則后面的讀請求自然會從數(shù)據(jù)庫中讀取新值然后回填緩存。
方案的弊端
結(jié)合雙刪策略+緩存超時設(shè)置,這樣最差的情況就是在超時時間內(nèi)數(shù)據(jù)存在不
一致,而且又增加了寫請求的耗時。
2,異步更新緩存(基于訂閱binlog的同步機制)
技術(shù)思路:
MySQL binlog增量訂閱消費+消息隊列+增量數(shù)據(jù)更新到redis
讀Redis:熱數(shù)據(jù)基本都在Redis
寫MySQL:增刪改都是操作MySQL
更新Redis數(shù)據(jù):MySQ的數(shù)據(jù)操作binlog,來更新到Redis
Redis更新
1)數(shù)據(jù)操作主要分為兩大塊:
一個是全量(將全部數(shù)據(jù)一次寫入到redis)
一個是增量(實時更新)
這里說的是增量,指的是mysql的update、insert、delate變更數(shù)據(jù)。
2)讀取binlog后分析,利用消息隊列,推送更新各臺的redis緩存數(shù)據(jù)。
這樣一旦MySQL中產(chǎn)生了新的寫入、更新、刪除等操作,就可以把binlog相關(guān)的消息推送至Redis,Redis再根據(jù)binlog中的記錄,對Redis進行更新。
其實這種機制,很類似MySQL的主從備份機制,因為MySQL的主備也是通過binlog來實現(xiàn)的數(shù)據(jù)一致性。
這里可以結(jié)合使用canal(阿里的一款開源框架),通過該框架可以對MySQL的binlog進行訂閱,而canal正是模仿了mysql的slave數(shù)據(jù)庫的備份請求,使得Redis的數(shù)據(jù)更新達到了相同的效果。
其實還可以采用其他第三方工具:kafka、rabbitMQ等來實現(xiàn)推送更新Redis。