1. 緩存使用目的
緩存由于其高并發和高性能的特性,已經在項目中被廣泛使用(IO cost比資料庫低了幾個數量級,redis是從記憶體中讀取資料, mysql是從磁盤讀取資料)
2. 讀取資料
流程無異議:
- 判斷是否有緩存
- 有,直接傳回資料給調用端. 無,跳到3
- 從資料庫加載資料,有資料直接寫入緩存,無資料則跳到4
- 若不寫入緩存, 可能發生緩存穿透(緩存穿透是指查詢一個一定不存在的資料,由于緩存是不命中時需要從資料庫查詢,查不到資料則不寫入緩存,這将導緻這個不存在的資料每次請求都要到資料庫去查詢,造成緩存穿透。)。可以緩存空對象來解決緩存穿透問題。
- 傳回空資料。
3. 更新緩存
3.1更新政策
- 先更新資料庫,再更新緩存
- 先删除緩存,再更新資料庫
- 先更新資料庫,再删除緩存
3.2 政策分析
- 先更新資料庫,再更新緩存
技術角度分析: 多線程更新資料庫和緩存時,并不能保證次序,比如A線程先發起,然後B線程後發起,但是B線程可能比A線程先更新緩存,這就導緻了緩存最後的值是A線程的值導緻資料庫和緩存不一緻
業務角度分析:1. 加入業務是寫多讀少的場景,采用這種方案就會導緻,資料壓根還沒讀到,緩存就被頻繁的更新,浪費性能。2. 加入寫入緩存的值是要在資料庫更新後,經過一系列計算再更新緩存的,采用此方案會造成性能浪費。
- 先删除緩存,再更新資料庫
比如一個寫線程A, 一個讀線程B,在A線程删除緩存後,B線程發現緩存不存在就去讀取資料庫舊值,然後更新緩存,然後A線程才更新資料庫,如此也會造成資料庫緩存不一緻。
- 先更新資料庫,再删除緩存
這種方式同樣有并發通路問題。同樣2個線程,緩存失效,線程A探知緩存失效,查詢資料庫,取得舊值,線程B更新資料庫并且删除緩存key,線程A将舊值寫入緩存。
但一般來說資料庫IO遠遠慢比緩存IO,是以一般線程A寫入緩存會發生線上程B删除緩存之前。
另外還有一個問題就是删除緩存不成功,用重試機制保障删除操作,一個是用消息隊列,另一個是訂閱binlog日志來更新緩存(canal)。