1. 简介
在高并发场景下,数据库IO往往会成为服务性能瓶颈,而且高并发写入争用同一数据资源时很容易将数据库打挂掉。服务器编程中,通过引入缓存将数据的读写操作都放到内存中,一方面由于减少了数据库访问,解决了高并发场景下的数据库瓶颈问题,另一方面由于内存数据访问速度远远高于磁盘IO,可以大幅提高数据的访问速度和服务性能。
从广泛的角度来说,可以将驻留在“内存”中的数据都视作缓存,而根据缓存与应用进程是否属于同一实例,又可将缓存分成本地缓存和分布式缓存。
本地缓存:在服务进程的内存空间中缓存数据,数据读写都在同一个进程内。
分布式缓存:独立于服务进程部署,需要通过网络来完成分布式缓存数据传输。
本地缓存
优缺点
访问速度快,但无法进行大数据存储 本地缓存占用的是应用进程内的空间,相对于分布式缓存它不需要跨网络传输,性能会更好;但同时由于进程空间大小的限制,注定了本地缓存不支持大数据量的数据存储。 数据会随应用进程重启而丢失 实例重启时,应用进程内存空间本地缓存的数据会丢失,所以对于本地缓存数据使用时要注意不能随应用进程的重启而丢失。 可能存在数据不一致 本地缓存只支持被该应用进程访问,无法被其他应用进程访问。如果在应用进程的集群部署当中,当数据更新需要同步不同部署节点的本地缓存数据来保证数据一致性时,复杂度高且很容易出问题。
实现
使用 hashmap 是最简单基础方式,是 JDK 的自带类,因为其内部为 KV 结构,存储对应的键值对,在需要时,可以通过 key 获得相应的 value,因为不是线程安全的类,所以在需要保证线程安全时可以使用 ConcurrentHashMap。这种方式使用简单但是对象的有效性和周期性不能保证(也就是没有缓存淘汰算法),容易造成内存的急剧上升,适用于业务简单,数据量小且对并发量没有太高要求的场景。
Cache工具:Caffeine,除了缓存功能外,还具备定时失效、重新加载等特性。
GuavaCache
Guava 是 google 开源的一个公共 java 库,类似于 Apache Commons,它提供了集合,反射,缓存,科学计算,xml,io 等一些工具类库。cache 只是其中的一个模块。使用 GuavaCache 能够方便快速的构建本地缓存。 实现原理基于 ConcurrentHashMap,提供了三种缓存回收方式(基于容量回收、定时回收和基于引用回收)来限制内存的占用量;
Caffeine
Caffeine 是 Java8 对 Guava 缓存的重写版本, 支持多种缓存过期策略,底层数据存储使用的也是面向 JDK8 的 ConcurrentHashMap(增加了红黑树,在 hash 冲突时也保持读性能)。Guava Cache 功能虽然强大,但是只是对 LRU 的一层封装,在复杂的业务场景下,LRU 淘汰策略显得力不从心。为此基于 W-TinyLFU(LFU+LRU 算法的变种) 淘汰策略的进程内缓存 —— Caffeine Cache 诞生了。也就是说 Caffeine 与 GuavaCache 相比,在功能基本一致的情况下,通过对算法和部分逻辑的优化完成了对性能和缓存命中率的提升。
适用场景
本地缓存一般适用于数据量较小且频率可见的访问场景尤其适用于缓存只读数据,如果数据在不同的实例机器上需要共享和保持一致,就需使用分布式缓存而非本地缓存来统一存储,所有实例都在分布式缓存中进行统一数据读取和管理。
本地缓存适用场景
- 单个应用程序实例:适用于单个应用程序实例,不需要跨实例共享数据。
- 高性能要求:对访问速度要求极高,且数据量不大。
- 简单应用:应用逻辑简单,不需要复杂的缓存管理和数据同步。
分布式缓存
优缺点
支持大数据量存储,不受应用进程重启影响 分布式缓存是独立部署的进程,拥有自身独立的内存空间,不会受到应用服务进程重启的影响。分布式缓存支持以集群的方式扩展,因此可以进行大数据量的缓存。 数据集中存储,保证数据一致 应用进程实例部署时,每一个部署节点都是从分布式缓存中进行数据存取,不存在像本地缓存那样数据更新时多实例数据不一致问题。 数据读写分离,高性能、高可用 分布式缓存可以实现读写分离,可以解决高并发场景下中的数据读写性能问题。由于多个缓存节点冗余存储数据,能够提高缓存数据的可用性,避免某个缓存节点宕机导致的数据不可用问题。 数据需要跨网络传输,性能低于本地缓存 分布式缓存独立于服务应用进程,需要通过网络进行数据传输,相对于本地缓存进程内部的数据读取操作,性能会较低。
实现
目前一般是使用Redis作为分布式缓存,能够存储更丰富的k-v数据结构,而且由于Redis是单线程的,不存在并发数据读写的线程安全问题,能够保证数据读写操作的顺序性。
适用场景
在高并发场景或对于较大其不可预见的用户访问时,采用分布式缓存,如在某些短时热门活动中可以将Redis当做数据库来用。
分布式缓存适用场景
-
多实例部署:适用于多实例部署,需要跨实例共享数据。
-
大数据量:数据量较大,需要利用多个节点的内存资源。
-
高可用性要求:对系统的高可用性和数据一致性有较高要求。
-
复杂应用:应用逻辑复杂,需要高级的缓存管理和数据同步功能。