微服务化后缓存怎么作

摘要

最近接手的代码中遇到几个缓存的问题,存在一些设计原则的问题,这里总结一下,但愿能够对你有帮助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

相关文章
相关标签/搜索