框架基本用法

常用组件

其他

使用多级缓存

单纯使用进程内缓存和分布式缓存都存在各自的不足。如果需要更高的性能以及更好的可用性,我们可以将缓存设计为多级结构。将最热的数据使用进程内缓存存储在内存中,进一步提升访问速度。

这个设计思路在计算机系统中也存在,比如 CPU 使用 L1、L2、L3 多级缓存,用来减少对内存的直接访问,从而加快访问速度。一般来说,多级缓存架构使用二级缓存已可以满足大部分业务需求,过多的分级会增加系统的复杂度以及维护的成本。因此,多级缓存不是分级越多越好,需要根据实际情况进行权衡。

一个典型的二级缓存架构,可以使用进程内缓存(如:Caffeine/Google Guava/Ehcache/HashMap)作为一级缓存;使用 Redis 缓存作为二级缓存。有童鞋可能好奇,Redis 已经那么快了为什么不直接使用它呢?这又回到前面的问题上,具体说,是因为访问 Redis 需要网络传输,增加了网络开销。因此 Redis 作为二级缓存,应该尽量减少 Redis 的访问次数,尽量减少网络传输的成本。说白了,就算 Redis 再快也快不过本地进程内(也就是内存内)的速度。

缓存查询

多级缓存查询流程如下:

  1. 首先,查询 L1 缓存,如果缓存命中,直接返回结果;如果没有命中,执行下一步。
  2. 接下来,查询 L2 缓存,如果缓存命中,直接返回结果并回填 L1 缓存;如果没有命中,执行下一步。
  3. 最后,查询数据库,返回结果并依次回填 L2 缓存、L1 缓存。

多级缓存更新

对于 L1 缓存,如果有数据更新,只能删除并更新所在机器上的缓存,其他机器只能通过超时机制来刷新缓存。超时设定可以有两种策略:

对于 L2 缓存,如果有数据更新,其他机器立马可见。但是,也必须要设置超时时间,其时间应该比 L1 缓存的有效时间长。为了解决进程内缓存不一致的问题,设计可以进一步优化;

通过消息队列的发布、订阅机制,可以通知其他应用节点对进程内缓存进行更新。使用这种方案,即使消息队列服务挂了或不可靠,由于先执行了数据库更新,但进程内缓存过期,刷新缓存时,也能保证数据的最终一致性。

现实一个吧~

受到前辈一文《SpringBoot,用 200 行代码完成一个一二级分布式缓存》所启发,于是尝试直接实现一个,但过程中发现若干问题:

因此须考虑一个淘汰算法实现的,例如常见的 LRU 算法。另外,这里加入带过期时间的缓存,能否解决问题呢?笔者认为是否定的,因为虽然可以加入自动过期时间的缓存,但缓存总数的上限没有限制,可以加入成千上万甚至更多的缓存,这是不行的。