快取架構設計細節二三事--究竟先操作快取,還是資料庫?
快取架構設計細節二三事
原創: 58沈劍 架構師之路 2016-03-08
本文主要討論這麼幾個問題:
(1)“快取與資料庫”需求緣起
(2)“淘汰快取”還是“更新快取”
(3)快取和資料庫的操作時序
(4)快取和資料庫架構簡析
一、需求緣起
場景介紹
快取是一種提高系統讀效能的常見技術,對於讀多寫少的應用場景,我們經常使用快取來進行優化。
例如對於使用者的餘額資訊表account(uid, money),業務上的需求是:
(1)查詢使用者的餘額,SELECT money FROM account WHERE uid=XXX,佔99%的請求
(2)更改使用者餘額,UPDATE account SET money=XXX WHERE uid=XXX,佔1%的請求
由於大部分的請求是查詢,我們在快取中建立uid到money的鍵值對,能夠極大降低資料庫的壓力。
讀操作流程
有了資料庫和快取兩個地方存放資料之後(uid->money),每當需要讀取相關資料時(money),操作流程一般是這樣的:
(1)讀取快取中是否有相關資料,uid->money
(2)如果快取中有相關資料money,則返回【這就是所謂的資料命中“hit”】
(3)如果快取中沒有相關資料money,則從資料庫讀取相關資料money【這就是所謂的資料未命中“miss”】,放入快取中uid->money,再返回
快取的命中率 = 命中快取請求個數/總快取訪問請求個數 = hit/(hit+miss)
上面舉例的餘額場景,99%的讀,1%的寫,這個快取的命中率是非常高的,會在95%以上。
那麼問題來了
當資料money發生變化的時候:
(1)是更新快取中的資料,還是淘汰快取中的資料呢?
(2)是先操縱資料庫中的資料再操縱快取中的資料,還是先操縱快取中的資料再操縱資料庫中的資料呢?
(3)快取與資料庫的操作,在架構上是否有優化的空間呢?
這是本文關注的三個核心問題。
二、更新快取 VS 淘汰快取
什麼是更新快取:資料不但寫入資料庫,還會寫入快取
什麼是淘汰快取:資料只會寫入資料庫,不會寫入快取,只會把資料淘汰掉
更新快取的優點:快取不會增加一次miss,命中率高
淘汰快取的優點:簡單(我去,更新快取我也覺得很簡單呀,樓主你太敷衍了吧)
那到底是選擇更新快取還是淘汰快取呢,主要取決於“更新快取的複雜度”。
例如,上述場景,只是簡單的把餘額money設定成一個值,那麼:
(1)淘汰快取的操作為deleteCache(uid)
(2)更新快取的操作為setCache(uid, money)
更新快取的代價很小,此時我們應該更傾向於更新快取,以保證更高的快取命中率
如果餘額是通過很複雜的資料計算得出來的,例如業務上除了賬戶表account,還有商品表product,折扣表discount
account(uid, money)
product(pid, type, price, pinfo)
discount(type, zhekou)
業務場景是使用者買了一個商品product,這個商品的價格是price,這個商品從屬於type類商品,type類商品在做促銷活動要打折扣zhekou,購買了商品過後,這個餘額的計算就複雜了,需要:
(1)先把商品的品類,價格取出來:SELECT type, price FROM product WHERE pid=XXX
(2)再把這個品類的折扣取出來:SELECT zhekou FROM discount WHERE type=XXX
(3)再把原有餘額從快取中查詢出來money = getCache(uid)
(4)再把新的餘額寫入到快取中去setCache(uid, money-price*zhekou)
更新快取的代價很大,此時我們應該更傾向於淘汰快取。
however,淘汰快取操作簡單,並且帶來的副作用只是增加了一次cache miss,建議作為通用的處理方式。
三、先運算元據庫 vs 先操作快取
OK,當寫操作發生時,假設淘汰快取作為對快取通用的處理方式,又面臨兩種抉擇:
(1)先寫資料庫,再淘汰快取
(2)先淘汰快取,再寫資料庫
究竟採用哪種時序呢?
還記得在《冗餘表如何保證資料一致性》文章(點選檢視)裡“究竟先寫正表還是先寫反表”的結論麼?
對於一個不能保證事務性的操作,一定涉及“哪個任務先做,哪個任務後做”的問題,解決這個問題的方向是:
如果出現不一致,誰先做對業務的影響較小,就誰先執行。
由於寫資料庫與淘汰快取不能保證原子性,誰先誰後同樣要遵循上述原則。
假設先寫資料庫,再淘汰快取:第一步寫資料庫操作成功,第二步淘汰快取失敗,則會出現DB中是新資料,Cache中是舊資料,資料不一致。
假設先淘汰快取,再寫資料庫:第一步淘汰快取成功,第二步寫資料庫失敗,則只會引發一次Cache miss。
結論:資料和快取的操作時序,結論是清楚的:先淘汰快取,再寫資料庫。
四、快取架構優化
上述快取架構有一個缺點:業務方需要同時關注快取與DB,有沒有進一步的優化空間呢?有兩種常見的方案,一種主流方案,一種非主流方案(一家之言,勿拍)。
主流優化方案是服務化:加入一個服務層,向上遊提供帥氣的資料訪問介面,向上遊遮蔽底層資料儲存的細節,這樣業務線不需要關注資料是來自於cache還是DB。
非主流方案是非同步快取更新:業務線所有的寫操作都走資料庫,所有的讀操作都總快取,由一個非同步的工具來做資料庫與快取之間資料的同步,具體細節是:
(1)要有一個init cache的過程,將需要快取的資料全量寫入cache
(2)如果DB有寫操作,非同步更新程式讀取binlog,更新cache
在(1)和(2)的合作下,cache中有全部的資料,這樣:
(a)業務線讀cache,一定能夠hit(很短的時間內,可能有髒資料),無需關注資料庫
(b)業務線寫DB,cache中能得到非同步更新,無需關注快取
這樣將大大簡化業務線的呼叫邏輯,存在的缺點是,如果快取的資料業務邏輯比較複雜,async-update非同步更新的邏輯可能也會比較複雜。
五、其他未盡事宜
本文只討論了快取架構設計中需要注意的幾個細節點,如果資料庫架構採用了一主多從,讀寫分離的架構,在特殊時序下,還很可能引發資料庫與快取的不一致,這個不一致如何優化,後續的文章再討論吧。
六、結論強調
(1)淘汰快取是一種通用的快取處理方式
(2)先淘汰快取,再寫資料庫的時序是毋庸置疑的
(3)服務化是向業務方遮蔽底層資料庫與快取複雜性的一種通用方式
相關文章
- 究竟先操作快取,還是資料庫?快取資料庫
- 到底是先更新資料庫還是先更新快取?資料庫快取
- 到底應該先操作快取還是先運算元據庫?快取
- 併發環境下,先運算元據庫還是先操作快取?快取
- 快取,究竟是淘汰,還是修改?快取
- 架構設計(三):引入快取架構快取
- Python操作Redis快取資料庫PythonRedis快取資料庫
- 小工匠聊架構 - 分散式快取技術_快取設計架構分散式快取
- 簡事二三 之 http快取機制HTTP快取
- 系統架構設計:程式快取和快取服務,如何抉擇?架構快取
- 快取淘汰、快取穿透、快取擊穿、快取雪崩、資料庫快取雙寫一致性快取穿透資料庫
- Java高併發快取架構,快取雪崩、快取穿透之謎Java快取架構穿透
- 詳談分散式系統快取的設計細節分散式快取
- CPU快取是什麼?一二三級快取哪個對CPU最重要?快取
- 巧妙設計多級快取,為資料庫減負快取資料庫
- 如何設計快取系統:快取穿透,快取擊穿,快取雪崩解決方案分析快取穿透
- 漫談Web快取架構Web快取架構
- 多級快取架構(六)快取架構
- Vue 全站快取二:如何設計全站快取Vue快取
- 你管這破玩意叫快取穿透?還是快取擊穿?快取穿透
- 什麼是redis快取雪崩、快取穿透、快取擊穿Redis快取穿透
- 快取穿透、快取雪崩和快取擊穿是什麼?快取穿透
- 分散式快取--快取與資料庫一致性方案分散式快取資料庫
- Redis不僅僅是快取,還是……Redis快取
- 分散式系統關注點——先寫DB還是「快取」?分散式快取
- 分散式快取架構綜述分散式快取架構
- 給資料庫減負刻不容緩:多級快取設計資料庫快取
- 前端快取那些事前端快取
- 快取注意事項快取
- Redis快取資料庫-快速入門Redis快取資料庫
- Caffeine 快取庫快取
- 快取穿透、快取擊穿、快取雪崩、快取預熱快取穿透
- Java進階專題(十八) 系統快取架構設計 (下)Java快取架構
- Java進階專題(十七) 系統快取架構設計 (上)Java快取架構
- Redis快取穿透、擊穿、雪崩,資料庫與快取一致性Redis快取穿透資料庫
- 快取架構,一篇足夠?快取架構
- 快取穿透、快取擊穿、快取雪崩快取穿透
- 快取穿透、快取雪崩、快取擊穿快取穿透