本地缓存

经过前面两篇文章的介绍,我们知道了缓存的重要性以及分布式系统的基础概念,接下来我们就要开始正式学习一些主流的缓存系统。

首先来简单了解缓存系统的分类,缓存系统按照缓存数据的存储方式可以分为本地缓存集中式缓存两种。

本地缓存:缓存数据保存在单个应用服务器中,各个应用服务器之间的缓存数据是独立的。 集中式缓存:缓存数据保存在专门的缓存服务器中,应用服务器通过网络请求从缓存服务器获取缓存数据。

本地缓存

集中式缓存

本地缓存与集中式缓存最大的区别是数据读取性能以及数据一致性。

本地缓存是将数据保存在应用服务器的内存中,因此读取速度非常快。而集中式缓存则多了一个网络请求的时间消耗,千万不要小看这些网络消耗,如果读取次数非常频繁,网络消耗的时间可能比将数据从内存读取出来的时间还要长。

我们的系统都会有一些配置数据,比如外围系统的接口地址、常量信息、页面标签的国际化文字等等。这些数据很少变更,而且数据量不大,占用内存少,因此就适合用本地缓存来存储,如果用集中式缓存就有点大炮打蚊子,大材小用了。

但是由于本地缓存的数据是以应用服务器为单位分开存储的,因此就可能出现一台应用服务器的数据有更新,而其他服务器数据没有更新,导致数据不一致的问题。集中式缓存则可以通过集中管理缓存数据的方式,保证数据的一致性。

保证数据一致性是使用缓存经常会遇到的问题。假如我们有多台应用服务器,都用本地缓存的方式提前缓存了用户资料,里面有用户的姓名,电话,住址等信息。

某一天,用户修改了自己的电话号码,这个请求被A服务器接收到了,它就更新了电话号码,但是其它服务器并不知道这个变更,电话号码还是旧的。这样在一套系统中,出现同一个人有两个不同电话号码的情况,数据不一致了。

但是如果使用集中式缓存,就不存在这样的问题,集中式缓存会保证在任意时刻,所有应用服务器读取到的用户资料都是一样的。

这篇文章,就让我们通过学习Ehcache来了解本地缓存的一些具体特性。

Ehcache的特点

Ehcache是一个用Java实现的简单、高速、线程安全的缓存管理类库。用“小巧玲珑”四个字来形容Ehcache最合适不过了,因为它本身的jar包很小(Ehcache 2.2.3才668KB),依赖少(唯一的依赖就是SLF4J),配置简单,API也易于使用,调用起来非常便捷。

正是由于“小巧玲珑”,Ehcache被广泛地用于Hibernate、Spring、Cocoon等其它开源系统中。比如Hibernate,就将Ehcache作为默认的CacheProvider。

麻雀虽小,但却五脏俱全,Ehcache的特点不少,主要有以下几点:

  1. 快速。Ehcache的线程机制是为大型高并发系统设计的,这一点使得使其在性能上具备一定的优势,而且在过去众多的测试已经表明,Ehcahce是最快的Java缓存之一。
  2. 简单。要使用Ehcache只需要在配置文件中进行简单的配置即可。一个典型的Ehcache配置文件内容如下: Ehcache配置 如果结合spring使用,配置项更少: spring中Ehached额配置

  3. 提供多种缓存策略。Ehcache支持LRU(最近最少使用)、LFU(最少被使用)和FIFO(先进先出)三种缓存策略,程序员可以根据系统需求自主选择不同的缓存策略。
  4. 支持两级数据缓存。数据可以同时缓存在内存和磁盘中,这样一方面扩充了缓存容量,可以以GB为单位缓存数据。另一方面也能够支持缓存数据持久化,在虚拟机重启时从磁盘读取缓存数据到内存,解决虚拟机重启/停机后缓存数据丢失的问题。
  5. 支持分布式缓存。可以通过RMI、可插入API等方式进行分布式缓存。不过需要特别指出的是,虽然Ehcache提供了分布式缓存功能,具备了一些集中式缓存的特性,但是其功能与Memcached或者Redis相比仍然有所欠缺,我们更多的还是将Ehcache作为一个本地缓存使用。

Ehcache架构

Ehcache架构图如下,总共分为四个模块。 Ehcache架构

  1. Cache Replicaitong:负责缓存同步的实现,包括TerraCotta、RMI、JMS和JGroup四种方式。
  2. In-Process APIS:提供对外常用的API,包括JRuby、Hibernate、JMX、SOAP API、Cache Server五种API。
  3. Network APIS:提供Ehcache的通信协议,有Restfull API、SOAP API和JMX API等。
  4. Ehcache Core:核心代码部分,包括CacheManager缓存管理器、Cache缓存的具体实现、Element缓存内容、SOR(system of record)获取真实数据的组件等。

Ehcache的使用场景

在实际的工作环境中,我们应该结合缓存框架自身的特点以及系统使用场景,来决定应该使用哪一种缓存。

先说说Ehcahce的局限性,在哪些场景下我们应该尽量避免使用Ehcache?

  1. 数据一致性要求高。文章一开始我就为大家介绍了,由于本地缓存框架数据分开存储的特性,无法保证数据一致性,因此在一致性要求较高的情况下,我们应该避免使用Ehcache,转而尝试Redis、Memcached等集中式缓存。
  2. 数据容灾/安全要求高。有一些缓存数据比较重要敏感,一旦丢失就会对业务造成不可逆的影响。这样的缓存数据是需要在系统中进行备份容灾的,最常见的做法就是采用集群方式将缓存数据分别备份在不同服务器上,这样就涉及到数据同步,而数据同步是Ehcahce的一大软肋,因此在这种情况下,我们也要尽量避免使用Ehcahce。

那么在什么时候使用Ehcache比较好呢?一种比较常见的做法就是将Ehcache作为集中式缓存(Memcached/Redis)的二级本地缓存。

项目中读取缓存的流程如下: 缓存读取流程

这个流程存在一个风险,如果集中缓存系统因为某些原因宕机,造成服务无法访问,那么将会有大量请求直接穿透到数据库,对数据库造成巨大的压力。

此时我们就可以将Ehcache作为二级本地缓存,将集中缓存系统返回的结果保存在本地,这样即使集中缓存系统宕机,应用服务器还可以使用本地缓存应对请求。

这个方案还存在一个问题,就是当数据变更后,如何保证集中缓存与本地缓存的数据一致,应对方法也很多,比如让本地缓存定期失效,定时刷新或者应用服务器主动通知,在这里就不展开讲了。

总结

Ehcache由于其轻巧,便捷的特点,在很多的开源框架中得到广泛运用,而且对于应用系统开发来讲,Ehcache也可以满足大部分的需求。在数据一致性、安全性要求不高的情况下,推荐优先使用Ehcache。

后续的文章中,我将继续为大家介绍集中式缓存Memcached和Redis,敬请期待。

上篇集中式缓存
下篇分布式系统