直接硬核乾貨,去掉前戲。redis
方案大體說明數據庫
1:假設對redis中存在一對key,value的對應關係是 key=money,value=666緩存
2:當修改線程修改key時先將key設置成value=666_write,(這裏須要說明的是:線上實際應用能夠將_wirte改爲很是複雜的UUID等字符串,只要保證不要和線上實際set的值衝突便可,例如666_write_@#$%^&*_qwerty服務器
3:當讀取時發現key的存在讀取標識666_read_@#$%^&*_qwerty,發現有其餘讀取線程已經在從DB中load數據了,這種狀況休眠一下重試就能夠了,不須要從DB中load數據,由於這樣作會引發大量線程訪問數據庫引起災難。若是不存在讀取標識則判斷一下是否含有寫入標識若不存在則直接返回數據便可,若存在讀取標識將全部讀取標識去掉以後看看是否爲空,不爲空則返回當前value便可。併發
4:爲何一個字符串會出現多個寫入標識呢。假設線程A和B對 key=money,value=666的數據進行修改,那麼線程A將value變成了value=666_write_@#$%^&*_qwerty,這個時候線程B也來摻和一下,會變成666_write_@#$%^&*_qwerty_write_@#$%^&*_qwerty,這個地方是須要注意的。同時也會出現業務數據被刪除了這個時候加上了修改標記,value變成_write_@#$%^&*_qwerty這種只有修改標識可是沒有業務數據的的狀況。線程
5:還有一個須要特別注意的是setnx在客戶端2.8以後 支持超時時間,這塊須要設置一個超時的時間處理,防止讀取線程setnx以後服務器掛了,引發永遠沒法同步的問題,至於超時時間設置成多少和實際環境有關。設計
6:同時也會出現_read_@#$%^&*_qwerty_write_@#$%^&*_qwerty這種同時帶有read和write的狀況,出如今併發讀取的場景。對象
7:這種須要DB緩存強一致讀寫應該在master中進行,不該該在slave中讀,頗有可能出現延遲致使問題blog
8:redis主從切換這種極端的場景出如今讀取或者修改的狀況下也會引發數據不一致的問題,通常上線這種問題的解決方案是跨機房多機緩存或者容許短時間內不一致的job任務定時校驗兜底等。字符串
總結起來就是一下幾點:
當存在_read_@#$%^&*_qwerty時說明有讀取線程正請求DB,重新load對象。
當存在_write_@#$%^&*_qwerty時說明當前key正在被寫入。
當存在多個_write_@#$%^&*_qwerty時 存在併發修改。
當有_read_@#$%^&*_qwerty和_write_@#$%^&*_qwerty時 存在讀和寫的併發。
讀取詳細設計方案
更新時設計方案:
其餘問題加羣討論吧 QQ羣號:825199617。