只有光頭才能變強。
文本已收錄至個人GitHub倉庫,歡迎Star: https://github.com/ZhongFuCheng3y/3y
回顧前面:html
今天來分享一下Redis幾道常見的面試題:java
回顧一下咱們爲何要用緩存(Redis):git
如今有個問題,若是咱們的緩存掛掉了,這意味着咱們的所有請求都跑去數據庫了。github
在前面學習咱們都知道Redis不可能把全部的數據都緩存起來(內存昂貴且有限),因此Redis須要對數據設置過時時間,並採用的是惰性刪除+按期刪除兩種策略對過時鍵刪除。Redis對過時鍵的策略+持久化面試
若是緩存數據設置的過時時間是相同的,而且Redis剛好將這部分數據所有刪光了。這就會致使在這段時間內,這些緩存同時失效,所有請求到數據庫中。redis
這就是緩存雪崩:shell
緩存雪崩若是發生了,極可能就把咱們的數據庫搞垮,致使整個服務癱瘓!數據庫
對於「對緩存數據設置相同的過時時間,致使某段時間內緩存失效,請求所有走數據庫。」這種狀況,很是好解決:設計模式
對於「Redis掛掉了,請求所有走數據庫」這種狀況,咱們能夠有如下的思路:緩存
好比,咱們有一張數據庫表,ID都是從1開始的(正數):
可是可能有黑客想把個人數據庫搞垮,每次請求的ID都是負數。這會致使個人緩存就沒用了,請求所有都找數據庫去了,但數據庫也沒有這個值啊,因此每次都返回空出去。
緩存穿透是指查詢一個必定 不存在的數據。因爲緩存不命中,而且出於容錯考慮,若是從 數據庫查不到數據則不寫入緩存,這將致使這個不存在的數據 每次請求都要到數據庫去查詢,失去了緩存的意義。
這就是緩存穿透:
緩存穿透若是發生了,也可能把咱們的數據庫搞垮,致使整個服務癱瘓!
解決緩存穿透也有兩種方案:
當咱們從數據庫找不到的時候,咱們也將這個空對象設置到緩存裏邊去。下次再請求的時候,就能夠從緩存裏邊獲取了。
參考資料:
緩存系列文章--5.緩存穿透問題
上面講緩存穿透的時候也提到了:若是從數據庫查不到數據則不寫入緩存。
通常咱們對讀操做的時候有這麼一個固定的套路:
若是僅僅查詢的話,緩存的數據和數據庫的數據是沒問題的。可是,當咱們要更新時候呢?各類狀況極可能就形成數據庫和緩存的數據不一致了。
從理論上說,只要咱們設置了鍵的過時時間,咱們就能保證緩存和數據庫的數據最終是一致的。由於只要緩存數據過時了,就會被刪除。隨後讀的時候,由於緩存裏沒有,就能夠查數據庫的數據,而後將數據庫查出來的數據寫入到緩存中。
除了設置過時時間,咱們還須要作更多的措施來儘可能避免數據庫與緩存處於不一致的狀況發生。
通常來講,執行更新操做時,咱們會有兩種選擇:
首先,要明確的是,不管咱們選擇哪一個,咱們都但願這兩個操做要麼同時成功,要麼同時失敗。因此,這會演變成一個分佈式事務的問題。
因此,若是原子性被破壞了,可能會有如下的狀況:
若是第一步已經失敗了,咱們直接返回Exception出去就行了,第二步根本不會執行。
下面咱們具體來分析一下吧。
操做緩存也有兩種方案:
通常咱們都是採起刪除緩存緩存策略的,緣由以下:
基於這兩點,對於緩存在更新時而言,都是建議執行刪除操做!
正常的狀況是這樣的:
若是原子性被破壞了:
若是在高併發的場景下,出現數據庫與緩存數據不一致的機率特別低,也不是沒有:
要達成上述狀況,仍是說一句機率特別低:
由於這個條件須要發生在讀緩存時緩存失效,並且併發着有一個寫操做。而實際上數據庫的寫操做會比讀操做慢得多,並且還要鎖表, 而讀操做必需在寫操做前進入數據庫操做,而又要晚於寫操做更新緩存,全部的這些條件都具有的機率基本並不大。
對於這種策略,實際上是一種設計模式:Cache Aside Pattern
刪除緩存失敗的解決思路:
正常狀況是這樣的:
若是原子性被破壞了:
看起來是很美好,可是咱們在併發場景下分析一下,就知道仍是有問題的了:
因此也會致使數據庫和緩存不一致的問題。
併發下解決數據庫與緩存不一致的思路:
咱們能夠發現,兩種策略各自有優缺點:
先刪除緩存,再更新數據庫
先更新數據庫,再刪除緩存(Cache Aside Pattern
設計模式)
能夠用databus或者阿里的canal監聽binlog進行更新。
參考資料:
緩存更新的套路
如何保證緩存與數據庫雙寫時的數據一致性?
分佈式之數據庫和緩存雙寫一致性方案解析
Cache Aside Pattern
這是幾道Redis常見的面試題,但願你們看完有所幫助,順利拿到offer!
樂於輸出 乾貨的Java技術公衆號:Java3y。200多篇 原創技術文章、海量視頻資源、精美腦圖!
精彩回顧:
以爲個人文章寫得不錯,不妨點一下贊!