Java高并发--缓存
主要是学习慕课网实战视频《Java并发编程入门与高并发面试》的笔记
在下图中每一个部分都可以使用缓存的技术。
缓存的特征
缓存命中:直接通过缓存获取到数据
命中率:命中数 / (命中数+ 未命中数)
最大元素(空间):超过最大空间将触发缓存清空策略
清空策略:FIFO(先进先出)、LFU(最少使用)、LRU(最近最少使用)、过期时间、随机
缓存的粒度越小,缓存命中率越高,对比缓存一整个集合和集合中的某一个元素,或者一整个对象和对象的其中一个属性。
缓存适合的业务场景:读多写少,实时性要求越低,越适合缓存。因为实时性高,数据经常会表更新修改。
缓存的分类
- 本地缓存:编程实现(成员变量、局部变量、静态变量)、Guava Cache。与程序耦合性高,各个应用需要维护自己的本地缓存
- 分布式缓存:Memcache、Redis。与程序是隔离的,缓存自身就是一个独立的应用。多个应用可以共享缓存。
缓存一致性
对数据实时性高的应用,要求数据库和缓存中的数据一致,这就比较依赖缓存的更新和更新策略了,一般在会在数据更改的时候主动更新缓存中的数据或者移除最近的缓存,这时候就可能出现缓存一致性问题。一般有以下四种情况。
缓存并发问题
缓存未命中时会尝试从后端数据库获取数据,在高并发的场景下可能会给数据库造成极大的冲击甚至导致缓存雪崩的现象,此外在某个缓存的key在被更新时,会被大量请求获取,也可能造成缓存一致性问题。如何解决呢?可以使用类似于锁的机制。在缓存更新或者过期的情况下,某个请求尝试获得锁,其他的请求必须等待,当从数据库获取完毕后再释放锁。
缓存穿透
在高并发场景下,对某个key的并发访问,没有命中缓存,出于容错性的考虑会从后端数据库中获取数据,导致大量请求对后端数据库的访问。当该key对应的数据本身是空的情况下,使得数据库中进行了大量不必要的查询操作,从而产生了大量的冲击和压力。
可以有以下几种策略:
- 对于空对象也进行缓存,这样避免请求穿透到数据库了。需要保证缓存数据的时效性。这种实现简单,比较适合命中不高但可能被频繁更新的数据。
- 单独过滤处理,对所有对应数据为空的key进行统一的存放,并在请求前作拦截,避免请求穿透到后端数据库。这种实现相对复杂,比较适合命中不高且更新不频繁的数据。
缓存雪崩
在说缓存雪崩时,先看看缓存抖动。缓存抖动比缓存雪崩更轻微的故障,通常是由于缓存结点的故障导致,推荐的做法是通过一致性哈希算法解决。缓存并发、缓存穿透、缓存抖动等都有可能导致缓存雪崩的发生。
缓存雪崩是由于缓存的原因导致大量请求到达后端数据库,从而导致数据库崩溃的灾难。