分散式的環境下, MySQL和Redis如何保持數據的一致性?
一台MySQL,一台Redis,兩台應用伺服器,用戶的數據存儲持久化在MySQL中,緩存在Redis,有請求的時候從Redis中獲取緩存的用戶數據,有修改則同時修改MySQL和Redis中的數據。現在問題是:
1. 先保存到MySQL和先保存到Redis都面臨著一個保存成功而另外一個保存失敗的情況,這樣,如何保證MySQL與Redis中的數據同步?2. 兩台應用伺服器的並發訪問,如何保證數據的安全性?
如果要「保證」數據的安全性,那麼會帶來開銷的進一步提升,以至於使用redis帶來的性能優勢都會喪失。正確的做法是區分不同的業務,使得並不需要「保證」數據一致性的場合,可以使用redis優化。而敏感的場合依然使用mysql。
說個大概吧,我們熱數據基本都是redis,增刪改都是操作mysql,對於讀是保存到redis,這樣就涉及到數據同步操作,同步操作分為兩大塊,我們的叫法是,一個是全量(將全部數據一次寫入到redis,時間幾小時不等),一個是增量(實時更新)。這裡說的是增量,主要問題是即時性,因為增刪改都是直接操作mysql變更都在MySQL(這裡高並發的問題是用分庫分表加外層的負載均衡) 所以我們的方向是讀取binlog然後分析 ,利用消息推送到某伺服器A,再進行分析,然後更新各台redis,消息推送工具用的是rabbitMQ,可設定某表的變更推送(分三類update insert delate 包含變更前後的數據),這裡有個問題是:mysql數據操作太頻繁產生的推送可能會很多,所以分析處理腳本處理速度一定要跟得上(我用Python寫,前期多線程(坑),後來改成多進程),還有一個問題是,對於mysql-redis的數據關係映射設定不要太複雜,一表對一表就行,數據組合交給業務層做,這樣分析處理腳本不會太多負擔,處理速度更快,而且操作redis也更簡單,redis每個對應mysql數據表的可使用多埠多實例,redis是單線程而且這樣對於redis的主從和負載均衡有利,
題外話:對於伺服器A 可以再給其它服務做一個數據表增量變更數據獲取介面,利用數據緯度,獲取時間段的變更數據。
追加,對於訂單類部分,都是完全使用mysql,這個做好數據伺服器,DB,table,分區,的拆分就好了,看並發請求越多拆分越多。
上面說太多都是屁話,其實就是MySQL binlog增量訂閱消費+消息隊列+處理並把數據更新到redis
一個簡單的例子。https://github.com/liukelin/canal_mysql_nosql_syncRedis只用作cache,寫請求只交給MySQL處理。否則你就要自己去解決一個分散式事務的問題,這個目前還沒有性價比高的解決方案。如果應用是write heavy的,請使用HBase和Cassandra。
緩存只做失效 不做更新
讀從redis讀,寫從mysql寫,之後通知redis對應的緩存失效,去資料庫緩存新數據。
高一致性的情況,就不要搞緩存
首先redis是作為緩存的,一般作為緩存有兩種用途,快速訪問和減少IO頻率,所謂減少IO頻率就是等緩存積累到一定大小然後一次刷入磁碟進行持久化。一般的設計就是客戶端往資料庫里更新或者寫讀數據,redis做為經常需要被讀取的數據或者被修改數據的緩存,提高操作效率,一般的操作應該是客戶端要修改數據時,先去緩存REDIS找,找不到的話去資料庫讀取,替換不熱的緩存,不熱的緩存刷回資料庫;能找到的話直接修改,這不存在一致性問題。然而LZ要並發訪問REDIS和SQL,這樣要保持一致性的話,讀的時候就不能寫了,就是客戶端更新REDIS,然後REDIS回寫資料庫,這是一個事務,如果有一步不成功,那麼整個事務不成功;如果是多客戶端,如果數據木有分塊,那麼所有寫操作串列話,如果對數據進行了合理分塊,同一塊數據數據串列寫,不同塊並發處理。如果保證讀的時候不能寫,這就需要一個主控節點來提供分散式鎖。分散式知識太多啦,我就粗燥寫了幾句!
你應該描述一下業務需求,12306要求是強一致,一般不會有這種要求,比如淘寶也就是最終一致,我們的做法是插入數據導mysql,從redis讀取數據,如果redis沒有,從mysql讀取到redis再從redis取出來
要求絕對的強一致肯定是不能用緩存了,可以將數據按 Key 進行分片進而打散請求,最終降低請求負載,起到和緩存一樣的功效。
同意一樓的說法,一致性要求高就不要往一邊寫,從另一邊讀了,另外如果允許redis數據相對於mysql有一定延遲的話可以寫一個同步服務,從mysql讀取binlog,分解出數據寫到redis中,這樣你的應用服務就不需要往兩邊寫了
先寫mysql,再刪除redis數據~
緩存這個東西永遠是能加最好,不能加不強求,畢竟它的存在只是為了效率,確保MySQL的數據就可以了。
如果不用Redis類似的分散式緩存,將MySQL伺服器的內存增大,然後做橫向擴展+只讀分離,是否也可以解決樓主說的問題?
在寫的時候先刪除redis的數據,然後更新mysql資料庫,在刪除和更新的語句上都添加異常捕獲,處理異常的時候都做成刪除redis數據語句。
這個,貌似用 memcached 比較好,雖然都有辦法解決,但是 mmc 簡單粗暴。
數據一致性的問題《Improving cache consistency》里有詳細的談論,最終的方案跟 @liu kelin 的一致:讀Redis,寫MySQL,同時消費MySQL binlog更新Redis。
為什麼沒人提Multi 部分業務需要在寫的時候避免讀請求進來,這時候可以用Multi,但也要看業務場景是否試用
我們的系統用REDIS作為庫存數據的讀寫,已經不要求和實際資料庫的強一致性,以REDIS為準,對REDIS的庫存操作會以批量的方式非同步寫到資料庫,對於同一庫存數據多次讀寫合併後寫入資料庫
如果不用實時得到最新數據,可以讀redis,寫mysql ,線程定時讀mysql時間戳,發現需要更新數據,更新redis。這種方案的好處是數據訪問時間不會出現抖動。
更新數據時候刪除redis,而不是更新
推薦閱讀:
※手機遊戲產業中,「開發商」、「運營商」、「渠道商」分別指什麼?
※作為手游開發商,如何看待與手游渠道商利益分配嚴重衝突的問題?
※我是做cocos2dx遊戲開發的,想轉ios開發,想聽聽大家的看法?
※手機遊戲開發職業如何規劃?
※從事遊戲開發工作(中小遊戲),但感覺市場不是很看好,我是否應該辭職?