您的位置:首页 > 数据库 > Redis

Redis之缓存设计

2017-11-25 11:00 204 查看
一 缓存的收益和成本

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等等。

在缓存失效的瞬间,大量线程来重建缓存,造成后端负载加大,甚至崩溃。

方案一:互斥锁

只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据

 

方案二:永不过期

不设过期时间
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐