缓存的类型及使用场景
-
本地缓存:在进程所在的内存中进行缓存,比如:使用Map实现在JVM堆中缓存。也可以使用ehcache这样的工具实现。
优点:能直接在heap区内读写,最快也最方便。
缺点:同样是受heap区域影响,缓存的数据量非常有限,同时缓存时间受GC影响。
主要满足单机场景下的小数据量缓存需求,同时对缓存数据的变更无需太敏感,如一般配置管理、基础静态数据等场景 -
分布式缓存:指的是与应用分离的缓存组件或服务,其最大的优点是自身就是一个独立的应用,与本地应用隔离,多个应用可直接的共享缓存。缺点:需要远程请求,性能不如本地缓存。
-
多级缓存(实际中常用):本地缓存+分布式缓存,本地缓存只保存访问频率最高的热点数据,其他的热点数据放在分布式缓存中。
缓存的常见淘汰策略
无论是本地缓存还是分布式缓存都是使用内存保存数据,这样可以保证较高的性能,由于内存容量的限制,当缓存的数据超出缓存容量时,就需要淘汰缓存数据。常见的淘汰策略有:
-
FIFO(first in first out)先入先出淘汰最早的数据
最先进入缓存的数据在缓存空间不够的情况下(超出最大元素限制)会被优先被清除掉,以腾出新的空间接受新的数据策略算法主要比较缓存元素的创建时间。在数据实效性要求场景下,可选择该类策略,优先保障最新数据可用 -
LFU(least frequently used)最不常用的数据
无论是否过期,根据元素的被使用次数判断,清除使用次数较少的元素释放空间
策略算法主要比较元素的hitCount(命中次数)。在保证高频数据有效性场景下,可选择这类策略 -
LRU(least recently used)最近没有使用的数据
无论是否过期,根据元素最后一次被使用的时间戳,清除最远使用时间戳的元素释放空间
策略算法主要比较元素最近一次被get使用时间。在热点数据场景下较适用,优先保证热点数据的有效性
常见的缓存问题
- 缓存更新方式
概念:缓存更新数据采用什么方式
产生原因:数据变更了,数据时效性导致数据变动
解决方法:同步更新、失效更新、异步更新、定时更新 - 缓存不一致
概念:缓存与数据库数据不一致,缓存的主从节点数据不一致
产生原因:主动同步更新失败、异步更新失败
如:更新数据库后更新redis,因为网络原因,请求超时
解决办法:增加重试,增加更新失败补偿任务 - 缓存穿透
概念:访问一个一定不存在的key,缓存不起作用,请求会穿透到数据库,流量大时数据库会挂掉
产生原因:访问一个不存在的key,请求会经过服务端去数据库查询。
如:恶意攻击访问
解决办法:
(1)bloomfilter(布隆过滤器),redis本身不带布隆过滤器,需要通过安装插件来实现。教程。
(2)使用redis自带数据结构bitmap,使用一个bitmap存储存在的key,bitmap中没有存在相应的key记录则过滤掉请求,这个方式最主要的问题是选一个好的算法计算偏移量。
访问key未在DB查询到值,也将空值写进缓存,但要设置较短过期时间,最长不超过5分钟(因为缓存会占用内存)。 - 缓存击穿
概念:访问一个热点key,但该key设置了过期时间,造成热点key失效,大量请求会穿透到数据库
产生原因:热点key过期失效
解决办法:
(1)使用互斥锁更新缓存,保证同一个进程中针对同一个数据不会并发访问数据库,减小数据库压力。
(2)使用随机退避方式更新,如果访问缓存数据失败,随机sleep一个时间后再次请求,如果仍然失败再请求数据库更新缓存。
还有一种特殊情况:大量的热点key由于设置了相同的过期时间,造成在同一时间大量的热点key集体失效,这会造成缓存雪崩,因此要差异化key的失效时间。 - 缓存雪崩
概念:指在某一个时间段,缓存集中过期失效。
产生原因:缓存挂掉
解决办法:采用主从模式、哨兵模式、集群模式保证缓存的高可用。
Redis在早期版本本身是不支持集群的,需要借助第三方工具比如Twemproxy、Codis和一致性哈希算法实现。而Redis3.0版本及以后支持集群部署模式,他自己引入了哈希槽的概念。看这里,redis cluster