缓存技术4之进程内缓存
进程内缓存
通过了客户端,CDN,负载均衡器,我们终于来到了应用服务器。应用服务器上部署着一个个应用,这些应用以进程的方式运行着,那么在进程中的缓存是怎样的呢?
进程内缓存又叫托管堆缓存,以 Java 为例,这部分缓存放在 JVM 的托管堆上面,同时会受到托管堆回收算法的影响。
由于其运行在内存中,对数据的响应速度很快,通常我们会把热点数据放在这里。
在进程内缓存没有命中的时候,我们会去搜索进程外的缓存或者分布式缓存。这种缓存的好处是没有序列化和反序列化,是最快的缓存。缺点是缓存的空间不能太大,对垃圾回收器的性能有影响。
目前比较流行的实现有 Ehcache、GuavaCache、Caffeine。这些架构可以很方便的把一些热点数据放到进程内的缓存中。
这里我们需要关注几个缓存的回收策略,具体的实现架构的回收策略会有所不同,但大致的思路都是一致的:
-
FIFO(First In First Out):先进先出算法,最先放入缓存的数据最先被移除。
-
LRU(Least Recently Used):最近最少使用算法,把最久没有使用过的数据移除缓存。
-
LFU(Least Frequently Used):最不常用算法,在一段时间内使用频率最小的数据被移除缓存。
在分布式架构的今天,多应用中如果采用进程内缓存会存在数据一致性的问题。
这里推荐两个方案:
-
消息队列修改方案
-
Timer 修改方案
消息队列修改方案
应用在修改完自身缓存数据和数据库数据之后,给消息队列发送数据变化通知,其他应用订阅了消息通知,在收到通知的时候修改缓存数据。
消息队列修改方案简图
Timer 修改方案
为了避免耦合,降低复杂性,对“实时一致性”不敏感的情况下。每个应用都会启动一个 Timer,定时从数据库拉取最新的数据,更新缓存。
不过在有的应用更新数据库后,其他节点通过 Timer 获取数据之间,会读到脏数据。这里需要控制好 Timer 的频率,以及应用与对实时性要求不高的场景。
Timer 修改方案简图
进程内缓存有哪些使用场景呢?
-
场景一:只读数据,可以考虑在进程启动时加载到内存。当然,把数据加载到类似 Redis 这样的进程外缓存服务也能解决这类问题。
-
场景二:高并发,可以考虑使用进程内缓存,例如:秒杀。
注:本文摘自51CTO技术栈