文章原創於公眾號:程式猿周先森。本平臺不定時更新,喜歡我的文章,歡迎關注我的微信公眾號。
可能談到保持Redis與Mysql雙庫的資料一致性,可能很多人最先想到的方案就是讀請求和寫請求序列化,串到一個記憶體佇列裡去。但是這個方案有著一個致命的缺點:讀請求和寫請求序列化會導致系統的吞吐量大幅度降低,需要使用比正常情況下多幾倍的機器去支撐線上的一個請求。Redis與Mysql雙庫的資料一致性問題為何會出現呢?其實我們可以考慮這麼一個業務場景:我們需要更新部分資料,我們首先更新資料庫資料,然後清除Redis快取中的資料。但是資料庫更新操作成功了,然而Redis清除快取出現異常了,這樣會導致出現這麼一種情況:資料庫中的資料已經更新為最新資料,但是Redis快取中的資料依舊還是老資料,這時候就會出現Redis與Mysql雙庫的資料一致性問題。
有些喜歡投機取巧的朋友就會想,那我先清掉快取中的舊資料,然後再寫入新資料到資料庫,最後更新快取不就可以了麼?這種方式可能出現一種問題:我們清除Redis快取成功了,但是寫入還未將新資料寫入到資料庫之前有讀請求的發生,就會導致資料庫中的舊資料再次存入Redis中,然後等到新資料寫入到資料庫後,一樣產生了。Redis與Mysql雙庫的資料一致性問題。
昨天談到Redis分散式環境其實有說到,分散式環境下,資料讀寫操作是併發操作,所以導致對用一份資料進行讀寫操作先後執行順序無法保證,所以就可能出現讀操作先於寫操作被執行,這時就會出現髒資料導致資料一致性問題的產生。這時候我們需要考慮我們讀取的資料是否是強一致性資料,比如賬戶餘額這種必須是強一致性資料,則讀資料庫,如果讀取的資料實時性沒有非常嚴格,比如積分排行榜等,就可以直接讀取Redis資料。如果機器併發量不高的情況下,讀取資料優先從Redis中讀取,快取中資料不存在才選擇從資料庫中獲取,並且把從資料庫獲取到的資料寫入Redis。寫入資料則相反,先清除Redis快取資料,再寫入資料到資料庫,如果是簡單資料這時候可以實時寫入到Redis中供讀操作讀取,如果是需要多表連表查詢的資料,則可以暫時不寫入Redis,等到有查詢操作的時候再寫入到Redis。
那如果是高併發的情況下呢?在高併發的情況下,讀取資料操作和上面是一樣,優先從Redis讀取。但是寫入資料操作就和剛才做法不一樣了,高併發的情況下,寫入資料先寫入到Redis,然後定期從Redis寫入到Mysql中。高併發的情況下需要注意的是,每個讀取資料的請求都需要在超時時間內返回資料,如果資料更新很頻繁,可能會導致Redis積壓了一系列更新操作,從而導致大量的讀取資料請求超時,最後這大量的讀取資料請求全部壓到資料庫,導致快取擊穿的現象產生,嚴重可能導致資料庫當機。這時候解決方案其實一般通過增加機器來增加吞吐量,或者暫時先返回一個老資料給客戶端。
所以到這裡我們其實方案很明確了,一共有兩種比較常見的方案:Redis是作為快取伺服器使用,一般作為快取有兩個用途:請求快速處理和減少I/O頻率。減少I/O頻率實際上就是剛才所說的高併發情況下資料實時寫入到資料庫,然後資料積累到一定程度定期寫入到資料庫,請求快速處理就是處理讀請求時有限從Redis中獲取,Redis是支援高併發操作的,所以處理速度很快,如果Redis中不存在資料,再去資料庫中查詢,然後寫入到redis中快取,以便二次讀取可以直接從快取中取到資料。第二種方案其實就是非同步非同步快取,Redis快取熱門資料,增刪改查都在Mysql操作,只要Mysql有insert、update、delete操作,可以通過kafka或者rabbitMQ等第三方訊息推送工具將binlog相關的訊息推送到Redis中,Redis解析binlog中的資料對Redis快取中的資料進行更新,Mysql中的主從備份機制也是通過binlog來實現資料一致性的。
本文由部落格一文多發平臺 OpenWrite 釋出!