最近项目上用到二级缓存,加上之前也使用过J2Cache之类的二级缓存开源框架,因此对二级缓存做一个简单的总结。

  1.         二级缓存解决什么问题?

目前缓存的解决方案一般有两种:

  • 内存缓存(如 Ehcache) —— 速度快,进程内可用
  • 集中式缓存(如 Redis)—— 可同时为多节点提供服务

二级缓存主要解决:

  1. 使用内存缓存时,一旦应用重启后,由于缓存数据丢失,缓存雪崩,给数据库造成巨大压力,导致应用堵塞
  2. 使用内存缓存时,多个应用节点无法共享缓存数据
  3. 使用集中式缓存,由于大量的数据通过缓存获取,导致缓存服务的数据吞吐量太大,带宽跑满。现象就是 Redis 服务负载不高,但是由于机器网卡带宽跑满,导致数据读取非常慢

二级缓存结构:

L1: 进程内缓存(caffeine\ehcache)
L2: Redis/Memcached 集中式缓存

数据读取:

  1. 读取顺序 -> L1 -> L2 -> DB, 通常采用Read Through模式
  2. redis里面二级缓存 redis二级缓存优化_redis里面二级缓存

缓存中数据不存在时,从DB中加载数据时需要加锁,避免缓存击穿。

缓存/DB中都不存在时,需要缓存空数据设置失效时间,避免缓存穿透

  1. 数据更新:
  • 更新数据库,再更新缓存

redis里面二级缓存 redis二级缓存优化_java_02

多线程场景下容易造成双写数据不一致,如下步骤 :

1.线程A先发起⼀个写操作,第⼀步先更新数据库   2. 线程B再发起⼀个写操作,第⼆步更新了数据库  3. 由于⽹络等原因,线程B先更新了缓存   4. 线程A更新缓存。

缓存保存的是A的数据(⽼数据),数据库保存的是B的数据(新数据)

更新缓存相对于删除缓存还有两点劣势:

如果你写⼊的缓存值,是经过复杂计算才得到的话。更新缓存频率⾼的话,就浪费性能。

在写多读少的情况下,数据很多时候还没被读取到⼜被更新了,这也浪费了性能

  • 删除缓存,更新数据库

redis里面二级缓存 redis二级缓存优化_分布式_03

1. 线程A发起⼀个写操作,第⼀步del cache   2. 此时线程B发起⼀个读操作,cache miss   3. 线程B继续读DB,读出来⼀个⽼数据   4. 然后线程B把⽼数据设置⼊cache  5. 线程A写⼊DB最新的数据

缓存和数据库的数据不⼀致了。缓存保存的是⽼数据,数 据库保存的是新数据 

  •    方式三:更新数据库,删除缓存

删除缓存时,需要广播通知删除对应的业务节点删除对应的L1本地缓存。

删除缓存采用常用的ZK监听机制/Redis的订阅机制/消息队列来通知业务节点删除L1缓存。

ZK的监听机制存在弊端,当发生Zk于业务节点发生断连时,没能及时通知到业务侧  【之前的系统就出过生产故障】,而且多引入了业务组件。

开源软件:https://gitee.com/ld/J2Cache