谈谈java中的缓存

写在前面
  说起缓存,记得曾看过最简单的一句话来描述缓存,缓存即空间换时间的一种方式。缓存主要是将程序中常使用的数据存储中一定的介质(如内存)等,以避免程序每次调用都要去和数据库交互,给程序的性能带来损耗。如果程序中存在缓存,那么在调用程序时就可以先看缓存中是否存在,不存在再去与数据库交互,提高了程序整体的运行的速度。接下来我们就来学习总结下缓存的分类、相关技术以及实现方式。
  缓存的分类
  在本文中,我们按照缓存的用途将缓存分为两种,一种是本地缓存;一种是分布式缓存。
  本地缓存指的是程序级别的缓存组件,它的特点是本地缓存和应用程序会运行在同一个进程中,所以本地缓存的操作会非常快,因为在同一个进程内也意味着不会有网络上的延迟和开销。本地缓存适用于单节点非集群的应用场景,它的优点是快,缺点是多程序无法共享缓存。
  分布式缓存将应用系统和缓存组件进行分离的缓存机制,这样多个应用系统就可以共享一套缓存数据了,它的特点是共享缓存服务和可集群部署,为缓存系统提供了高可用的运行环境,以及缓存共享的程序运行机制。
  缓存的实现技术:
  本地缓存的实现技术由 EhCache 和 Google 的 Guava 来实现。他们的特点如下:
  EhCache 是目前比较流行的开源缓存框架,是用纯 Java 语言实现的简单、快速的 Cache 组件。EhCache 支持内存缓存和磁盘缓存。
  Guava Cache 是 Google 开源的 Guava 里的一个子功能,它是一个内存型的本地缓存实现方案,提供了线程安全的缓存操作机制。Guava Cache 类似于 Map 集合的方式对键值对进行操作,只不过多了过期淘汰等处理逻辑。
  分布式缓存可以使用 Redis 或 Memcached 来实现。那么他们之间又有什么区别?
  Redis支持服务器端的数据操作:Redis相比Memcached来说,拥有更多的数据结构和并支持更丰富的数据操作,通常在Memcached里,你需要将数据拿到客户端来进行类似的修改再set回去。这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的GET/SET一样高效。所以,如果需要缓存能够支持更复杂的结构和操作,那么Redis会是不错的选择。
  memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是redis目前是原生支持cluster模式的,redis官方就是支持redis cluster集群模式的,比memcached来说要更好。
   redis是目前比较常用的缓存实现方式之一,因为redis在高效缓存的同时还支持持久化,是真的香。我也有专门的文章介绍redis相关知识,链接如下: 对redis感兴趣可深入学习。
   在缓存的实现中吗,常涉及一些缓存淘汰的算法,主要有LRU(Least Recently Used,最近很少使用)、LFU(Least Frequently Used,最近不常被使用)和 FIFO(First In First Out,先进先出)等。他们的特点如下:
  LRU 算法有一个缺点,比如说很久没有使用的一个键值,如果最近被访问了一次,那么即使它是使用次数最少的缓存,它也不会被淘汰;
   LFU 算法解决了偶尔被访问一次之后,数据就不会被淘汰的问题,它是根据总访问次数来淘汰数据的,其核心思想是“如果数据过去被访问多次,那么将来它被访问次数也会比较多”。因此 LFU 可以理解为比 LRU 更加合理的淘汰算法。
  下面介绍一些常见的缓存过期策略
  目前比较常见的过期策略有以下三种:定时删除、惰性删除、定期删除。
  定时删除是指在设置键值的过期时间时,创建一个定时事件,当到达过期时间后,事件处理器会执行删除过期键的操作。它的优点是可以及时的释放内存空间,缺点是需要开启多个延迟执行事件来处理清除任务,这样就会造成大量任务事件堆积,占用了很多系统资源。
  惰性删除不会主动删除过期键,而是在每次请求时才会判断此值是否过期,如果过期则删除键值,否则就返回 null。它的优点是只会占用少量的系统资源,缺点是清除不够及时,会造成一定的空间浪费。
  定期删除是指每隔一段时间检查一次数据库,随机删除一些过期键值。
  一般地,实现缓存的各种技术中都是搭配使用,如redis就是使用了定期删除和惰性删除这两种策略。
  介绍完了缓存,我们可以手动实现一个缓存的系统,主要思路如下:
  1.需要定义一个存放缓存值的实体类,这个类里包含了缓存的相关信息,比如缓存的 key 和 value,缓存的存入时间、最后使用时间和命中次数(预留字段,用于支持 LFU 缓存淘汰)
  2.定义缓存的载体,用一个 ConcurrentHashMap 保存缓存的 key 和 value 对象(缓存值的实体类)
  3.新增一个缓存操作的工具类,用于添加和删除缓存,
  4.最后再缓存启动时,开启一个无限循环的线程用于检测并删除过期的缓存。
  实现代码如下:

  下面介绍下项目中使用到的缓存技术?是如何使用的?

  缓存使用不当会造成什么后果呢?