概述
一 缓存的收益和成本
1.1 优点
加速读写;降低后端负载
1.2成本
# 数据不一致性:缓存层和存储层的数据存在着差异
# 代码维护成本:同时维护缓存层和存储层的逻辑
缓存的使用场景:
# 开销大的复杂计算:比如MySQL一些复杂的操作或者计算,如果不加缓存,MySQL在并发量大的时候,可能扛不住
# 加速请求响应
二 缓存的更新策略
2.1 LRU/LFU/FIFO 算法剔除
一般算法剔除是指缓存数目超过了指定 的预设值,如何对现有缓存的数据进行剔除。比如最近很少的缓存项,比如最近频繁使用的缓存想,或者先进的先踢出来
2.2 超时剔除
一般是给缓存数据设置过期时间,让其在过期后自动删除。
2.3 主动更新
一般是指对数据一致性要求较高,需要在真实数据更新后,立即更新缓存数据,当然维护成本比较高。
2.4 最佳实践
# 低一致性业务建议配置最大内存和淘汰策略
# 高一致性业务建议结合使用超时剔除和主动更新
三 穿透优化
缓存穿透:是指查询一个根本不存在的数据,缓存层和存储层都不会命中,通常我们不会将存储层查不到的数据写入缓存。
缓存穿透将导致不存在的数据每一次请求都会到存储层去查询,失去了缓存保护后端存储的意义,有可能导致后端存储负载加大,甚至宕机。
造成缓存穿透的原因,一般都是:
# 开发的代码写的有问题或者数据有问题
# 一些恶意的攻击或者爬虫等造成的空命中
如何解决呢?
方案一:缓存空对象
当缓存和存储层都没有查到的话,就将该数据缓存起来,之后再访问这个数据的时候,就直接从缓存中获取。
但是这样,明显有问题:就是空值做了缓存,那就意味着缓存层很可能缓存更多的key都是对应着空值,那么也就需要更多的内存空间。
方案二:布隆过滤器拦截
在访问缓存层和存储层之前,用布隆过滤器对key进行过滤,做第一层拦截。
四 无底洞
为什么在有的场景,水平添加节点性能不但没有好转反而下降了。这种现象称为缓存的无底洞现象。即更多的节点不代表跟高的性能,投入越多,不一定产出越高。
为什么?
一般来说我们加节点,使得集群的性能应该更好,但是为什么性能反而下降了呢?key-value数据库,一般采用哈希函数将key映射到各个节点,造成key的分布和业务无关,但是由于数据量和访问量的持续增长,造成需要添加大量节点做水平扩容,导致key-value分布到更多的节点上,对于Redis批量操作需要从不同节点获取,相比于单机批量操作只涉及一次网络操作,分布式批量操作会涉及到多次网络时间。
无底洞问题分析:
# 客户端一次批量操作会涉及多次网络操作,就意味着批量操作会随着节点的增多耗时会不断扩大
# 网络连接数变多,对接点的性能也有一定影响
4.1 串行命令
由于key均与分布在集群中各个节点上,因此无法使用mget命令一次性获取,所以通常来讲要获取N个key的值,就是逐次执行N个get命令,这种操作虽然简单,但是时间复杂度增高。他的操作时间=n次网络时间+n次命令时间
public List<String> serialMGet(List<String> keys) {
JedisCluster cluster = getCluster();
List<String> values = new ArrayList<>();
String value = null;
for (String key : keys) {
value = cluster.get(key);
values.add(value);
}
return values;
}
private static JedisCluster getCluster(){
Map<String,Integer> nodes = new HashMap<>();
nodes.put("192.168.3.200",7001);
nodes.put("192.168.3.201",7001);
nodes.put("192.168.3.202", 7001);
return JedisUtils.getJedisCluster(nodes);
}
4.2 串行IO
我们知道,Smart客户端会保存slot和节点的对应关系,有了这两个数据就可以将属于同一个节点的key进行归档,得到每一个节点的key子列表,之后对每一个节点执行mget或者pipeline操作。操作时间 = node次网络时间 + N次命令时间,比第一种方式呢好很多,但是如果节点数太多,还是有一定的问题。
五 雪崩
指的是缓存层由于某些原因挂了,或者不能提供服务,从而导致流量疯狂的涌入了后端存储层,存储层调用暴涨,造成存储层可能会级联宕机。
预防和解决雪崩效应的方案:
# 保证缓存层服务高可用性
# 依赖隔离组件,为后端限流或者降级
无论是缓存还是存储层都有出错的概率,可以将他们都视作资源。作为并发量较大的系统,假如有一个资源不可用,可能会造成线程全部阻塞,从而系统不可用。
降级机制在高并发系统中非常普遍:比如推荐服务,如果个性化推荐不可用,可以降级补充热点数据。在实际项目中,我们对重要的资源比如Redis,MySQL,HBase或者其他外部接口都进行隔离,每一种资源都单独运行在自己的线程池中,对其他服务没有影响。
六 热点key重建优化
缓存+过期策略一般情况下,可以加速读写,又保证了数据定期更新,可以满足大部分需求,但是如果有2个问题同时出现,可能造成较大的危害:
# 当前的key是一个热点key,并发量非常大
# 重建缓存不可能在短时间内完成,可能是一个复杂的SQL或者多次IO等等。
在缓存失效的瞬间,大量线程来重建缓存,造成后端负载加大,甚至崩溃。
方案一:互斥锁
只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据
方案二:永不过期
不设过期时间
最后
以上就是平淡小丸子为你收集整理的Redis之缓存设计的全部内容,希望文章能够帮你解决Redis之缓存设计所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复