聊聊本地缓存和分布式缓存

缓存 , 消息队列 , 分库分表是高并发解决方案三剑客 。
缓存之所以能够让系统“更快” , 本质上做到了如下两点:

  • 减小 CPU 消耗
    将原来需要实时计算的内容提前算好、把一些公用的数据进行复用 , 这可以减少 CPU 消耗 , 从而提升响应性能 。
  • 减小 I/O 消耗
    将原来对网络、磁盘等较慢介质的读写访问变为对内存等较快介质的访问 , 从而提升响应性能 。
对于应用系统来讲 , 我们经常将缓存划分为本地缓存和分布式缓存 。
本地缓存 :应用中的缓存组件 , 缓存组件和应用在同一进程中 , 缓存的读写非常快 , 没有网络开销 。但各应用或集群的各节点都需要维护自己的单独缓存 , 无法共享缓存 。
分布式缓存:和应用分离的缓存组件或服务 , 与本地应用隔离 , 多个应用可直接共享缓存 。
这篇文章 , 聊聊本地缓存和分布式缓存 , 希望大家读完之后 , 在面对不同的业务场景时 , 能够做出合理的缓存选型 。
一、本地缓存 JDK MapJDK Map 经常用于缓存实现:
  • HashMap
    HashMap 是一种基于哈希表的集合类 , 它提供了快速的插入、查找和删除操作 。可以将键值对作为缓存项的存储方式 , 将键作为缓存项的唯一标识符 , 值作为缓存项的内容 。
  • ConcurrentHashMap
    ConcurrentHashMap 是线程安全的 HashMap , 它在多线程环境下可以保证高效的并发读写操作 。
  • LinkedHashMap
    LinkedHashMap 是一种有序的 HashMap  , 它保留了元素插入的顺序 , 可以按照插入顺序或者访问顺序进行遍历 。
  • TreeMap
    TreeMap 是一种基于红黑树的有序 Map , 它可以按照键的顺序进行遍历 。
笔者曾经负责艺龙红包系统 , 红包活动就是存储在 ConcurrentHashMap 中  , 通过定时任务刷新缓存  。
聊聊本地缓存和分布式缓存

文章插图
核心流程:
1、红包系统启动后 , 初始化一个 ConcurrentHashMap 作为红包活动缓存 ;
2、数据库查询所有的红包活动 , 并将活动信息存储在 Map 中 ;
3、定时任务每隔 30 秒  , 执行缓存加载方法 , 刷新缓存 。
为什么红包系统会将红包活动信息存储在本地内存 ConcurrentHashMap 呢 ?
  • 红包系统是高并发应用 , 快速将请求结果响应给前端 , 大大提升用户体验;
  • 红包活动数量并不多 , 就算全部放入到 Map 里也不会产生内存溢出的问题;
  • 定时任务刷新缓存并不会影响红包系统的业务 。
笔者见过很多单体应用都使用这种方案 , 该方案的特点是简洁易用 , 工程实现也容易。
二、本地缓存框架虽然使用 JDK Map 能快捷构建缓存 , 但缓存的功能还是比较孱弱的 。
因为现实场景里 , 我们可能需要给缓存添加缓存统计、过期失效、淘汰策略等功能 。
于是 , 本地缓存框架应运而生 。
流行的 JAVA 缓存框架包括:Ehcache , google Guava ,  Caffeine Cache。
聊聊本地缓存和分布式缓存

文章插图
下图展示了 Caffeine 框架的使用示例 。
聊聊本地缓存和分布式缓存

文章插图
虽然本地缓存框架的功能很强大 , 但是本地缓存的缺陷依然明显 。
1、高并发的场景 , 应用重启之后 , 本地缓存就失效了 , 系统的负载就比较大 , 需要花较长的时间才能恢复;
2、每个应用节点都会维护自己的单独缓存 , 缓存同步比较头疼 。
三、分布式缓存分布式缓存是指将缓存数据分布在多台机器上 , 以提高缓存容量和并发读写能力的缓存系统 。分布式缓存通常由多台机器组成一个集群 , 每台机器上都运行着相同的缓存服务进程 , 缓存数据被均匀地分布在集群中的各个节点上 。


推荐阅读