究竟先操作緩存,還是數(shù)據(jù)庫?
緩存存儲,也是數(shù)據(jù)的冗余。
- 數(shù)據(jù)庫訪問數(shù)據(jù),磁盤IO,慢;
- 緩存里訪問數(shù)據(jù),存操作,快;
- 數(shù)據(jù)庫里的熱數(shù)據(jù),可在緩存冗余一份;
- 先訪問緩存,如果***,能大大的提升訪問速度,降低數(shù)據(jù)庫壓力;
這些,是緩存的核心讀加速原理。
但是,一旦沒有***緩存,或者一旦涉及寫操作,流程會比沒有緩存更加復雜,這些是今天要分享的話題。
讀操作,如果沒有***緩存,流程是怎么樣的?
答:如下圖所示
- 嘗試從緩存get數(shù)據(jù),結(jié)果沒有***;
- 從數(shù)據(jù)庫獲取數(shù)據(jù),讀從庫,讀寫分離;
- 把數(shù)據(jù)set到緩存,未來能夠***緩存;
讀操作的流程應該沒有歧義。
寫操作,流程是怎么樣的?
答:寫操作,既要操作數(shù)據(jù)庫中的數(shù)據(jù),又要操作緩存里的數(shù)據(jù)。
這里,有兩個方案:
- 先操作數(shù)據(jù)庫,再操作緩存;
- 先操作緩存,再操作數(shù)據(jù)庫;
并且,希望保證兩個操作的原子性,要么同時成功,要么同時失敗。
這演變?yōu)橐粋€分布式事務的問題,保證原子性十分困難,很有可能出現(xiàn)一半成功,一半失敗,接下來看下,當原子性被破壞的時候,分別會發(fā)生什么。
一、先操作數(shù)據(jù)庫,再操作緩存
如上圖,正常情況下:
- 先操作數(shù)據(jù)庫,成功;
- 再操作緩存(delete或者set),也成功;
但如果這兩個動作原子性被破壞:***步成功,第二步失敗,會導致,數(shù)據(jù)庫里是新數(shù)據(jù),而緩存里是舊數(shù)據(jù),業(yè)務無法接受。
畫外音:如果***步就失敗,可以返回調(diào)用方50X,不會出現(xiàn)數(shù)據(jù)不一致。
二、先操作緩存,再操作數(shù)據(jù)庫
如上圖,正常情況下:
- 先操作緩存(delete或者set),成功;
- 再操作數(shù)據(jù)庫,也成功;
畫外音:如果***步就失敗,也可以返回調(diào)用方50X,不會出現(xiàn)數(shù)據(jù)不一致。
如果原子性被破壞,會發(fā)生什么呢?
這里又分了兩種情況:
- 操作緩存使用set
- 操作緩存使用delete
使用set的情況:***步成功,第二步失敗,會導致,緩存里是set后的數(shù)據(jù),數(shù)據(jù)庫里是之前的數(shù)據(jù),數(shù)據(jù)不一致,業(yè)務無法接受。
并且,一般來說,數(shù)據(jù)最終以數(shù)據(jù)庫為準,寫緩存成功,其實并不算成功。
使用delete的情況:***步成功,第二步失敗,會導致,緩存里沒有數(shù)據(jù),數(shù)據(jù)庫里是之前的數(shù)據(jù),數(shù)據(jù)沒有不一致,對業(yè)務無影響。只是下一次讀取,會多一次cache miss。
畫外音:此時可以返回調(diào)用方50X。
最終,先操作緩存,還是先操作數(shù)據(jù)庫?
答:
(1) 讀請求,先讀緩存,如果沒有***,讀數(shù)據(jù)庫,再set回緩存
(2) 寫請求
- 先緩存,再數(shù)據(jù)庫
- 緩存,使用delete,而不是set
畫外音:《緩存,究竟是淘汰,還是修改?》也提到了,淘汰緩存還是修改緩存的建議。
希望大家有收獲,有不同方案歡迎討論。
末了,挖個坑:
在緩存讀取流程中,如果主從沒有同步完成,步驟二讀取到一個舊數(shù)據(jù),可能導致緩存里set一個舊數(shù)據(jù),最終導致數(shù)據(jù)庫和緩存數(shù)據(jù)不一致。
【本文為51CTO專欄作者“58沈劍”原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者】