微服務化後緩存怎麼作

摘要

最近接手的代碼中遇到幾個緩存的問題,存在一些設計原則的問題,這裏總結一下,但願能夠對你有幫助html

問題

問題1: 店鋪數據的獲取,將用戶關注的數據放在店鋪信息一塊兒返回

對外提供的接口web

List<Shop> getPageShop(final Query query,final Boolean cache);

返回的店鋪信息sql

public class Shop {

    public static final long DEFAULT_PRIORITY = 10L;

    /**
     * 惟一標識
     */
    private Long id;
   //省略了店鋪其餘信息
    /**
     * 用戶關注
     */
    private ShopAttention attention;
}

當調用方設置cache爲true時,由於有緩存的存在,獲取不到用戶是否關注的數據。數據庫

問題2: 統計店鋪的被關注數致使的慢SQL,致使數據庫cpu飆高,影響到了整個應用

SQL緩存

SELECT shop_id, count(user_Id) as attentionNumber
FROM shop_attention
WHERE shop_id IN
<foreach collection="shopIds" item="shopId" separator="," open="(" close=")">
    #{shopId}
</foreach>
GROUP BY shopId

這兩種代碼的寫法都是基於一個基準app

不一樣的地方的緩存策略不同,好比我更新的地方,查找數據時不能緩存,頁面展現的查找的地方須要緩存。 既然服務提供方不知道該不應緩存,那就無論了,交給調用方去管理微服務

這種假設自己沒什麼問題,可是忽略了另一個原則,服務的內聚性。不該該被外部知道的就不必暴露給外部設計

不管是面向過程的C,仍是面向對象的語言,都強調內聚性,也就是高內聚,低耦合。單體應用中應當遵循這個原則,微服務一樣遵循這個原則。可是在實際過程當中,咱們發現作到高內聚並不簡單。咱們必需要時時刻刻審視方法/服務的邊界,只有肯定好職責邊界,才能寫出高內聚的代碼code

問題分析

第一個問題,從緩存的角度來看,是忽略了數據的更新頻繁性以及數據獲取的不一樣場景。htm

對於店鋪這樣一個大的聚合根,自己包含的信息不少,有些數據可能會被頻繁更改的,有些則會不多更新的。那麼不一樣的修改頻率,是否緩存/緩存策略天然不一樣,使用同一個參數Boolean cache來控制顯然不妥

第二個問題,這種統計類的需求使用SQL統計是一種在數據量比較小的狀況下的權宜之計,當數據規模增大後,必需要使用離線計算或者流式計算來解決。它自己是一個慢SQL,因此必需要控制號調用量,這種統計的數據量的時效性應該由服務方控制,不須要暴露給調用方。不然就會出現上述的問題,調用方並不清楚其中的邏輯,不走緩存的話就會使得調用次數增長,QPS的增長會致使慢SQL打垮數據庫

解法

緩存更新自己就是一個難解的問題,在微服務化後,多個服務就更加複雜了。涉及到跨服務的多級緩存一致性的問題。

因此對大部分的業務,咱們能夠遵循這樣的原則來簡單有效處理。

  • 對數據的有效性比較敏感的調用都收斂到服務內部(領域內部應該更合適),不要暴露給調用方,
    領域內部作數據的緩存失效控制
  • 緩存預計算(有些頁面的地方不但願首次打開慢)的邏輯也應該放在領域內控制,不要暴露給調用方。
    在領域內部控制在不一樣的地方使用不一樣的緩存策略,好比更新數據的地方須要獲取及時的數據。好比商品的價格,和商品的所屬類目更新頻次不一樣,須要有不一樣的過時時間。
  • 跨服務調用爲了減小rpc調用,能夠再進行一層緩存。由於這些調用能夠接受過時的數據,再進行一層緩存沒問題,expired time疊加也沒多大影響(expire time在這邊主要是影響緩存的命中數)

以上述店鋪查詢問題改造爲例

在這裏插入圖片描述
擴展:若是後續有case在跨服務的調用時,對數據的過時比較敏感,而且在調用方也作了緩存,那就是跨服務的多級緩存一致性的問題。那就須要服務方告知調用方緩存什麼時候失效,使用消息隊列or其餘方式來實現。

關注【方丈的寺院】,與方丈一塊兒開始技術修行之路
在這裏插入圖片描述

參考

https://martin.kleppmann.com/2012/10/01/rethinking-caching-in-web-apps.html

相關文章
相關標籤/搜索