天天看點

緩存架構設計細節二三事

本文主要讨論這麼幾個問題:

(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)服務化是向業務方屏蔽底層資料庫與緩存複雜性的一種通用方式

七、關于infoQ

架構師之路并不好走。這需要過硬的技術能力、出色的綜合素質、靈活的大腦,更需要豐富的專業知識。

InfoQ是一家堅持原創的深度技術社群,其旗下的QCon、ArchSummit大會也享有盛名。走在架構師之路上的,都應該多關注這個微信号。他們,是前沿技術的傳播者。掃描二維碼關注InfoQ,回複關鍵字“女王”有驚喜!(據說有碼農界最漂亮的程式媛們的大尺度照片。)

==【完】==

緩存架構設計細節二三事