快取架構設計
需求分析
快取是一種提高系統讀效能的常見技術,對於讀多寫少的應用場景,我們經常使用快取來進行優化。
例如對於使用者的餘額資訊表account(uid, money),業務上的需求是:
查詢使用者的餘額,select money from account where uid=***,佔99%的請求
更改使用者餘額,update account set money=*** where uid=***,佔1%的請求
由於大部分的請求是查詢,我們在快取中建立uid到money的鍵值對,能夠極大降低資料庫的壓力。
讀操作流程
有了資料庫和快取兩個地方存放資料之後(uid->money),每當需要讀取相關資料時(money),操作流程一般是這樣的:
讀取快取中是否有相關資料,uid->money
如果快取中有相關資料money,則返回【這就是所謂的資料命中「hit」】
如果快取中沒有相關資料money,則從資料庫讀取相關資料money【這就是所謂的資料未命中「miss」】,放入快取中uid->money,再返回
快取的命中率 = 命中快取請求個數/總快取訪問請求個數 = hit/(hit+miss)
上面舉例的餘額場景,99%的讀,1%的寫,這個快取的命中率是非常高的,會在95%以上。
問題
當資料money發生變化的時候:
是更新快取中的資料,還是淘汰快取中的資料呢?
是先操縱資料庫中的資料再操縱快取中的資料,還是先操縱快取中的資料再操縱資料庫中的資料呢?
快取與資料庫的操作,在架構上是否有優化的空間呢?
更新快取 vs 淘汰快取
選擇的關鍵:更新快取的複雜度
情況一:只是簡單的把餘額money設定成乙個值
更新快取的代價很小,此時我們應該更傾向於更新快取,以保證更高的快取命中率。
情況二:如果餘額是通過很複雜的資料計算得出來的,例如業務上除了賬戶表account,還有商品表
product,折扣表discount
account(uid, money)
product(pid, type, price, pinfo)
discount(type, zhekou)
業務場景是使用者買了乙個商品product,這個商品的**是price,這個商品從屬於type類商品,type類商品在做**活動要打折扣zhekou,購買了商品過後,這個餘額的計算就複雜了,需要:
先把商品的品類,**取出來:select type, price from product where pid=***
再把這個品類的折扣取出來:select zhekou from discount where type=***
再把原有餘額從快取中查詢出來money = getcache(uid)
再把新的餘額寫入到快取中去setcache(uid, money-price*zhekou)
更新快取的代價很大,此時我們應該更傾向於淘汰快取。
建議
淘汰快取操作簡單,並且帶來的***只是增加了一次cache miss,建議作為通用的處理方式。
先運算元據庫 vs 先操作快取
這個比較主要是針對寫操作的,最根本性的問題就是保證資料的一致性,因為運算元據庫和快取不是原子性的,所以一旦中間出現什麼錯,有可能會導致資料不一致的情況,就要從下面情況來細說:
add資料時,應該先寫資料庫,還是先寫快取?
update資料時,如果選擇淘汰cache,應該先更新資料庫還是快取?
update資料時,如果選擇更新cache,應該先更細資料庫還是快取?
add資料時,怎麼選擇?
很顯然,這是啥先寫資料庫,然後再寫快取,即使後面寫快取失敗,頂多出現一次cache miss,最起碼資料庫已經持久化了;如果反過來,一旦寫資料失敗,那麼快取將會是髒資料,除非自己加上寫快取失敗時,delete掉資料庫的資料,這樣太麻煩了。
update資料時,選擇淘汰cache,那應該先更新哪個?
記住這個準則,如果出現不一致,誰先做對業務的影響較小,就誰先執行。下面對兩種情況進行業務上的比較:
假設先寫資料庫,再淘汰快取:第一步寫資料庫操作成功,第二步淘汰快取失敗,則會出現db中是新資料,cache中是舊資料,資料不一致。
假設先淘汰快取,再寫資料庫:第一步淘汰快取成功,第二步寫資料庫失敗,則只會引發一次cache miss。
結論
先淘汰快取,再寫資料庫。
update資料時,選擇更新cache,那應該先更新哪個?
細想,無論是先更新資料庫再更新快取,還是先更新快取再更新資料庫,一旦後面的操作失敗,都有可能出現資料不一致。所以,根據上面的解決方法,進行了改進:
情況一:更新或新增快取代價比較大
先刪除快取
然後更新資料庫
等下次查詢時,首先cache miss,查詢出資料後再增加快取
情況二:更新或新增快取代價小
先刪除快取
然後更新資料庫
再將新資料新增到快取
情況三:如果快取伺服器很穩定,基本上能夠保證修改或查詢不會出問題
更新資料庫
然後更新快取
快取架構優化
前面提到的快取架構有乙個缺點:業務方需要同時關注快取與db,有沒有進一步的優化空間呢?有兩種常見的方案,一種主流方案,一種非主流方案。
主流優化方案
服務化:加入乙個服務層,向上游提供帥氣的資料訪問介面,向上游遮蔽底層資料儲存的細節,這樣業務線不需要關注資料是來自於cache還是db。
非主流方案
非同步快取更新:業務線所有的寫操作都走資料庫,所有的讀操作都總快取,由乙個非同步的工具來做資料庫與快取之間資料的同步,具體細節是:
要有乙個init cache的過程,將需要快取的資料全量寫入cache
如果db有寫操作,非同步更新程式讀取binlog,更新cache
在(1)和(2)的合作下,cache中有全部的資料,這樣:
缺點
這樣將大大簡化業務線的呼叫邏輯,存在的缺點是,如果快取的資料業務邏輯比較複雜,async-update非同步更新的邏輯可能也會比較複雜。
快取架構設計
1 快取技術和框架的重要性 網際網路的一些高併發,高效能的專案和系統中,快取技術是起著功不可沒的作用。快取不僅僅是key value的簡單訪問,它在具體的業務場景中,還是很複雜的,需要很強的架構設計能力。我曾經就遇到過因為快取架構設計不到位,導致了系統崩潰的案例。2 快取的技術方案分類 1 是做實時...
快取架構設計細節二三事
本文主要討論這麼幾個問題 1 快取與資料庫 需求緣起 2 淘汰快取 還是 更新快取 3 快取和資料庫的操作時序 4 快取和資料庫架構簡析 一 需求緣起 場景介紹 快取是一種提高系統讀效能的常見技術,對於讀多寫少的應用場景,我們經常使用快取來進行優化。例如對於使用者的餘額資訊表account uid,...
快取架構設計細節二三事
1 快取與資料庫 需求緣起 2 淘汰快取 還是 更新快取 3 快取和資料庫的操作時序 4 快取和資料庫架構簡析 一 需求緣起 場景介紹 快取是一種提高系統讀效能的常見技術,對於讀多寫少的應用場景,我們經常使用快取來進行優化。例如對於使用者的餘額資訊表account uid,money 業務上的需求是...