為什么使用Redis做緩存
MySQL缺點
- 單機連接數目有限
- 對數據進行寫速度慢
Redis優點
- 內存操作數據速度快
- IO復用,速度快
- 單線程模型,避免線程切換帶來的開銷,速度快
一致性問題
讀數據的時候首先去Redis里讀,沒有讀到再去MySQL里讀,讀回來之后更新到Redis里作為下一次的緩存。寫數據的時候回產生數據不一致的問題,無論是先寫到Redis里再寫MySQL還是先寫MySQL再寫Redis,這兩步寫操作不能保證原子性,所以會出現Redis和MySQL里的數據不一致。無論采取何種方式都不能保證強一致性,如果對Redis里的數據設置了過期時間能夠保證最終一致性,對架構做的優化只能降低不一致性發生的概率,不能從根本上避免不一致性。
根據寫入的順序不同分為四種。
先刪緩存,再更新數據庫
這種操作的問題?
一般考慮某種策略的問題都是考慮該種策略會不會導致被刪除的臟數據由於時序混亂再次被讀線程從MySQL中讀出來。A線程寫數據,B線程讀數據,A線程刪除了緩存,B線程讀數據發現緩存沒有命中從數據庫中讀數據,B線程把讀出的舊數據寫到Redis里,A線程把新數據寫回去。和下面的先寫數據庫再刪緩存相比,這種方式顯著的缺點
- 先刪緩存,所以當兩個線程並發的時候很大幾率會出現緩存不命中,一旦緩存不命中,在寫線程修改MySQL完成之前讀進來的永遠是臟數據
- 在緩存到期之前Redis里一直是臟數據
解決策略
延遲雙刪,雙刪就是在更新完數據庫后再刪一次。不過延遲雙刪中更新數據庫之前的刪除還有什么意義?延遲的目的是為了刪除在寫MySQL期間讀線程可能把臟數據再次讀到Redis里,延遲的時間參照一次從MySQL讀數據並寫入Redis的時間
Cache Aside Pattern--先寫數據庫,再刪緩存
為什么更新數據庫后不更新而是刪除緩存?
- 更新緩存的操作不是必須的。可能緩存里的數據並沒有被讀到,就會被下一次更新MySQL操作帶來Redis更新操作覆蓋,那么本次更新操作就是無意義的。
- 更新緩存代價大。如果緩存里的數據不是把MySQL里的數據直接存下來,而是需要經過某種復雜的運算,那么這種不必要的更新會帶來更大的浪費。
這種操作的問題?
- 並發問題。A線程讀數據但沒有命中,B線程寫數據。A線程讀到了MySQL的舊數據,B線程寫了新的數據進MySQL,B線程刪除了Redis中舊數據的緩存,A用舊數據寫到了Redis緩存里。此時Redis里就是舊的臟數據。但這種case出現的概率較低,需要Redis緩存失效同時出現線程寫入操作,而且理論上A線程從MySQL中讀數據應該更快的返回。
解決方案?
每個寫MySQL線程寫完后延時一定時間在去刪Redis中的緩存。
Write Behind Caching Pattern--只更緩存,不更MySQL,MySQL由緩存異步的更新