如何保證https://www.b5b6.com/shujuku/和緩存數據的一致性?
先舉兩個例子,都是我做過的項目,應用場景都非常簡單:
曾經做過的一個保費試算的項目,業務場景是根據用戶基本信息+費率做計算,得到一個保費值,如果不了解這個業務場景的話,可以看做【用戶輸入】+【https://www.b5b6.com/shujuku/數據】+【一定的計算】=【結果】;這些數據復雜而且多,使用頻繁,但是每個產品的費率數據,幾乎是不會改變的(新增修改刪除都不會有):這些數據被我們放在Redis中,而且設置成永不失效;因為數據不會修改,那么https://www.b5b6.com/shujuku/和緩存中的數據肯定是一樣的。
現在做的項目中的一個功能,簡單描述業務場景:系統從N個業務系統中抽取數據,并做加工整合,加工完成后更新到https://www.b5b6.com/shujuku/中;這個場景也比較有特點,數據時效性要求很低(數據加工到https://www.b5b6.com/shujuku/已經晚了一天),數據變化的時間固定且唯一(批處理每天固定時間進行,只有一個批處理線程在跑);于是我們采用了一個比較保守的策略,就是設置數據在Redis中的過期時間;等數據過期后,當查詢的時候發現數據不在緩存中,再從https://www.b5b6.com/shujuku/中查詢出來后放入緩存。
現在想一想Redis中數據的更新策略有哪些(或者說緩存更新策略)?
給緩存設置過期時間,是我認為比較好的方式(這里不討論緩存雪崩的問題),這個【比較好】是在實現難度和數據一致性之間找到了一個平衡點;實現起來非常簡單,但是可能在數據變化后,緩存失效前的這段時間,https://www.b5b6.com/shujuku/和緩存之間的數據是不一致的(但最終會一致)。先更新https://www.b5b6.com/shujuku/,再更新緩存:有兩個比較大的問題,一是多線程的時候,可能會造成https://www.b5b6.com/shujuku/和緩存數據的不一致,而且這個不一致可能會是長期的。二是如果更新操作比較多的話,會頻繁地更新緩存。
A線程:set https://www.b5b6.com/shujuku/ key=1;B線程:set https://www.b5b6.com/shujuku/ key=100;B線程:set 緩存 key=100;A線程:set 緩存 key=1;(實際上,應該等于100,這樣就造成了緩存和https://www.b5b6.com/shujuku/中的數據不一致,并且如果數據不再更新,那么會使長期不一致)先刪緩存,再更新https://www.b5b6.com/shujuku/:這個會出現問題,還是A/B兩個線程,如果A刪除了緩存之后,在更新https://www.b5b6.com/shujuku/之前,B過來查詢不到緩存,于是就查詢了https://www.b5b6.com/shujuku/(A還沒更新),把數據寫入緩存,這時候A才把https://www.b5b6.com/shujuku/更新掉,這可能就會造成永久性的不一致。
先更新https://www.b5b6.com/shujuku/,再刪緩存:一樣的道理,大家可以自己想一想這個過程。
有些人可能覺得這個概率很小,怎么可能有這么巧的事情,恰好都是A做到一半,B插進來把全部的事情做完,A再去做后一半的操作;但是概率小,也是可能發生的,不得不考慮。
還有很多復雜的實現方案,比如更新完https://www.b5b6.com/shujuku/之后,把更新操作發給有序的消息隊列(不過過程越復雜,不一致的時間會越長),由另外的線程從隊列里面順序執行更新或刪除緩存的操作;
不過我認為,架構越復雜也越容易出錯,還不如根據實際的場景和團隊的開發水平,合理選擇緩存的使用,并給緩存設置合理的過期時間。
我將持續分享Java開發、架構設計、https://www.b5b6.com職業發展等方面的見解,希望能得到你的關注。