大家好,又见面了。
在服务端开发中,缓存常常被当做系统性能扛压的不二之选。在实施方案上,缓存使用策略虽有一定普适性,却也并非完全绝对,需要结合实际的项目诉求与场景进行综合权衡与考量,进而得出符合自己项目的最佳实践。
缓存使用的演进
现有这么一个系统:
一个互动论坛系统,用户登录系统之后,可以在论坛上查看帖子列表、查看帖子详情、发表帖子、评论帖子、为帖子点赞等操作。
系统中所有的配置数据与业务数据均存储在数据库
中。随着业务的发展,注册用户量越来越多,然后整个系统的响应速度也越来越慢,用户体验越来越差,用户逐渐出现流失。
本地缓存的牛刀小试
为了挽救这一局面,开发人员需要介入去分析性能瓶颈并尝试优化提升响应速度,并很快找到响应慢的瓶颈在数据库的频繁操作,于是想到了使用缓存
来解决问题。
于是,开发人员在项目中使用了基于接口维度的短期缓存,对每个接口的请求参数
(帖子ID)与响应内容
缓存一定的时间(比如1分钟),对于相同的请求,如果匹配到缓存则直接返回缓存的结果即可,不用再次去执行查询数据库以及业务维度的运算逻辑。
JAVA
中有很多的开源框架都有提供类似的能力支持,比如Ehcache
或者Guava Cache
、Caffeine Cache
等,可以通过简单的添加注解的方式就实现上述需要的缓存效果。比如使用Ehcache来实现接口接口缓存的时候,代码使用方式如下(这里先简单的演示下,后续的系列文档中会专门对这些框架进行深入的探讨):
@Cacheable(value="UserDetailCache", key="#userId")
public UserDetail queryUserDetailById(String userId) {
UserEntity userEntity = userMapper.queryByUserId(userId);
return convertEntityToUserDetail(userEntity);
}
复制代码
基上面的本地缓存策略改动后重新上线,整体的响应性能上果然提升了很多。本地缓存的策略虽然有效地提升了处理请求的速度,但新的问题也随之浮现。有用户反馈,社区内的帖子列表多次刷新后会出现内容不一致的情况,有的帖子刷新之后会从列表消失,多次刷新后偶尔会出现。
其实这就是本地缓存在集群多节点场景下会遇到的一个很常见的缓存漂移现象:
因为业务集群存在多个节点,而缓存是每个业务节点本地独立构建的,所以才出现了更新场景导致的本地缓存不一致的问题,进而表现为上述问题现象。
集中式缓存的初露锋芒
为了解决集群内多个节点间执行写操作之后,各节点本地缓存不一致的问题,开发人员想到可以构建一个集中式缓存,然后所有业务节点都读取或者更新同一份缓存数据,这样就可以完美地解决节点间缓存不一致的问题了。
业界成熟的集中式缓存有很多,最出名的莫过于很多人都耳熟能详的Redis
,或者是在各种面试中常常被拿来与Redis进行比较的Memcached
。也正是由于它们出色的自身性能表现,在当前的各种分布式系统中,Redis近乎已经成为了一种标配,常常与MySQL
等持久化数据库搭配使用,放在数据库前面进行扛压。比如下面图中示例的一种最简化版本的组网架构:
开发人员对缓存进行了整改,将本地缓存改为了Redis集中式缓存。这样一来:
-
缓存不一致问题解决:解决了各个节点间数据不一致的问题。
-
单机内存容量限制解决:使用了Redis这种分布式的集中式缓存,扩大了内存缓存的容量范围,可以顺便将很多业务层面的数据全部加载到Redis中分片进行缓存,性能也相比而言得到了提升。
似乎使用集中式缓存已经是分布式系统中的最优解了,但是现实情况真的就这么简单么?也不尽然!
多级缓存的珠联璧合
在尝到了集中式缓存的甜头之后,