【数据库】Redis集群篇

欢迎关注公众号:【爱编码】 若是有须要后台回复2019赠送1T的学习资料哦!!前端

哨兵模式

背景

当主服务器宕机后,须要手动把一台从服务器切换为主服务器,这就须要人工干预,费事费力,还会形成一段时间内服务不可用。这不是一种推荐的方式,更多时候,咱们优先考虑哨兵模式。node

定义

Sentinel(哨兵)是Redis 的高可用性解决方案:由一个或多个Sentinel 实例 组成的Sentinel 系统能够监视任意多个主服务器,以及这些主服务器属下的全部从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。面试

哨兵监听

server1故障

server1下线,server2升级为新的主服务器

实战配置

1.首先配置Redis的主从服务器,修改redis.conf文件以下redis

# 使得Redis服务器能够跨网络访问
bind 0.0.0.0
# 设置密码
requirepass "123456"
# 指定主服务器,注意:有关slaveof的配置只是配置从服务器,主服务器不须要配置
slaveof 192.168.11.128 6379
# 主服务器密码,注意:有关slaveof的配置只是配置从服务器,主服务器不须要配置
masterauth 123456
复制代码

上述内容主要是配置Redis服务器,从服务器比主服务器多一个slaveof的配置和密码。算法

  1. 配置3个哨兵,每一个哨兵的配置都是同样的。在Redis安装目录下有一个sentinel.conf文件,copy一份进行修改
# 禁止保护模式
protected-mode no
# 配置监听的主服务器,这里sentinel monitor表明监控,mymaster表明服务器的名称,能够自定义,192.168.11.128表明监控的主服务器,6379表明端口,2表明只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操做。
sentinel monitor mymaster 192.168.11.128 6379 2
# sentinel author-pass定义服务的密码,mymaster是服务名称,123456是Redis服务器密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster 123456
复制代码
  1. 有了上述的修改,咱们能够进入Redis的安装目录的src目录,经过下面的命令启动服务器和哨兵
# 启动Redis服务器进程
./redis-server ../redis.conf
# 启动哨兵进程
./redis-sentinel ../sentinel.conf
复制代码

注意启动的顺序。首先是主机(192.168.11.128)的Redis服务进程,而后启动从机的服务进程,最后启动3个哨兵的服务进程。后端

参考文章: www.jianshu.com/p/06ab9daf9…缓存

集群

搭建集群工做须要如下三个步骤:bash

1.准备节点

Redis集群通常由多个节点组成,节点数量至少为6个才能保证组成完整高可用的集群。每一个节点须要开启配置cluster-enabled yes,让Redis运行在集群模式下。建议为集群内全部节点统一目录,通常划分三个目录:conf、data、log,分别存放配置、数据和日志相关文件。把6个节点配置统一放在conf目录下服务器

#节点端口
port 6379
# 开启集群模式
cluster-enabled yes
# 节点超时时间,单位毫秒
cluster-node-timeout 15000
# 集群内部配置文件
cluster-config-file "nodes-6379.conf"
复制代码

其余配置和单机模式一致便可,配置文件命名规则redis-{port}.conf,准备好配置后启动全部节点,命令以下网络

redis-server conf/redis-6379.conf
redis-server conf/redis-6380.conf
redis-server conf/redis-6381.conf
redis-server conf/redis-6382.conf
redis-server conf/redis-6383.conf
redis-server conf/redis-6384.conf
复制代码

2.节点握手

节点握手是指一批运行在集群模式下的节点经过Gossip协议彼此通讯,达到感知对方的过程。节点握手是集群彼此通讯的第一步,由客户端发起命令:cluster meet{ip}{port}

图中执行的命令是:cluster meet127.0.0.16380让节点6379和6380节点进 行握手通讯。cluster meet命令是一个异步命令,执行以后马上返回。内部发起与目标节点进行握手通讯。

127.0.0.1:6379>cluster meet 127.0.0.1 6381
127.0.0.1:6379>cluster meet 127.0.0.1 6382
127.0.0.1:6379>cluster meet 127.0.0.1 6383
127.0.0.1:6379>cluster meet 127.0.0.1 6384
复制代码

最后执行cluster nodes命令确认6个节点都彼此感知并组成集群

127.0.0.1:6379> cluster nodes
4fa7eac4080f0b667ffeab9b87841da49b84a6e4 127.0.0.1:6384 master - 0 1468073975551
5 connected
cfb28ef1deee4e0fa78da86abe5d24566744411e 127.0.0.1:6379 myself,master - 0 0 0 connected
be9485a6a729fc98c5151374bc30277e89a461d8 127.0.0.1:6383 master - 0 1468073978579
4 connected
40622f9e7adc8ebd77fca0de9edfe691cb8a74fb 127.0.0.1:6382 master - 0 1468073980598
3 connected
8e41673d59c9568aa9d29fb174ce733345b3e8f1 127.0.0.1:6380 master - 0 1468073974541
1 connected
40b8d09d44294d2e23c7c768efc8fcd153446746 127.0.0.1:6381 master - 0 1468073979589
2 connected
复制代码

节点创建握手以后集群还不能正常工做,这时集群处于下线状态,全部的数据读写都被禁止。

3.分配槽

Redis集群把全部的数据映射到16384个槽中。每一个key会映射为一个固定的槽,只有当节点分配了槽,才能响应和这些槽关联的键命令。经过cluster addslots命令为节点分配槽。这里利用bash特性批量设置槽(slots)

redis-cli -h 127.0.0.1 -p 6379 cluster addslots {0...5461}
redis-cli -h 127.0.0.1 -p 6380 cluster addslots {5462...10922}
redis-cli -h 127.0.0.1 -p 6381 cluster addslots {10923...16383}
复制代码

关于集群伸缩、故障转移、节点通讯等知识。 可参考《Redis开发与运维》

缓存设计

穿透优化

缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中,一般出于容错的考虑,若是从存储层查不到数据则不写入缓存层。 整个过程分为以下3步

1.缓存层不命中。 2.存储层不命中,不将空结果写回缓存。 3.返回空结果

解决办法

1.缓存空对象 存储层不命中后,仍然将空对象保留到缓存层中,以后再访问这个数据将会从缓存中获取,这样就保护了后端数据源。

缓存空值应对穿透问题

缓存空对象会有两个问题: 第一,空值作了缓存,意味着缓存层中存了更多的键,须要更多的内存空间(若是是攻击,问题更严重),比较有效的方法是针对这类数据设置一个较短的过时时间,让其自动剔除。

第二,缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有必定影响。例如过时时间设置为5分钟,若是此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致,此时能够利用消息系统或者其余方式清除掉缓存层中的空对象。

相似代码实现以下:

String get(String key) {
// 从缓存中获取数据
String cacheValue = cache.get(key);
// 缓存为空
if (StringUtils.isBlank(cacheValue)) {
    // 从存储中获取
    String storageValue = storage.get(key);
    cache.set(key, storageValue);
    // 若是存储数据为空,须要设置一个过时时间(300秒)
    if (storageValue == null) {
      cache.expire(key, 60 * 5);
    }
    return storageValue;
} else {
    // 缓存非空
    return cacheValue;
  }
}
复制代码

2.布隆过滤器拦截

bloomfilter就相似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小,下面先来简单的实现下看看效果,我这里用guava实现的布隆过滤器:

<dependencies>  
     <dependency>  
         <groupId>com.google.guava</groupId>  
         <artifactId>guava</artifactId>  
         <version>23.0</version>  
     </dependency>  
</dependencies>  
复制代码
public class BloomFilterTest {
 
    private static final int capacity = 1000000;
    private static final int key = 999998;
 
    private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), capacity);
 
    static {
        for (int i = 0; i < capacity; i++) {
            bloomFilter.put(i);
        }
    }
 
    public static void main(String[] args) {
        /*返回计算机最精确的时间,单位微妙*/
        long start = System.nanoTime();
 
        if (bloomFilter.mightContain(key)) {
            System.out.println("成功过滤到" + key);
        }
        long end = System.nanoTime();
        System.out.println("布隆过滤器消耗时间:" + (end - start));
        int sum = 0;
        for (int i = capacity + 20000; i < capacity + 30000; i++) {
            if (bloomFilter.mightContain(i)) {
                sum = sum + 1;
            }
        }
        System.out.println("错判率为:" + sum);
    }
}
复制代码
成功过滤到999998
布隆过滤器消耗时间:215518
错判率为:318
复制代码

能够看到,100w个数据中只消耗了约0.2毫秒就匹配到了key,速度足够快。而后模拟了1w个不存在于布隆过滤器中的key,匹配错误率为318/10000,也就是说,出错率大概为3%,跟踪下BloomFilter的源码发现默认的容错率就是0.03:

public String getByKey(String key) {
    // 经过key获取value
    String value = redisService.get(key);
    if (StringUtil.isEmpty(value)) {
        if (bloomFilter.mightContain(key)) {
            value = userService.getById(key);
            redisService.set(key, value);
            return value;
        } else {
            return null;
        }
    }
    return value;
}
复制代码

更多知识可参考文章: blog.csdn.net/fanrenxiang…

雪崩优化

因为缓存层承载着大量请求,有效地保护了存储层,可是若是缓存层因为某些缘由不能提供服务,因而全部的请求都会达到存储层,存储层的调用量会暴增,形成存储层也会级联宕机的状况。缓存雪崩的英文原意是stampeding herd(奔逃的野牛),指的是缓存层宕掉后,流量会像奔逃的野牛同样,打向后端存储。

预防和解决缓存雪崩问题,能够从如下三个方面进行着手

1.保证缓存层服务高可用性。 和飞机都有多个引擎同样,若是缓存层设计成高可用的,即便个别节点、个别机器、甚至是机房宕掉,依然能够提供服务,例如前面介绍过的Redis Sentinel和Redis Cluster都实现了高可用

2.依赖隔离组件为后端限流并降级。

不管是缓存层仍是存储层都会有出错的几率,能够将它们视同为资源。做为并发量较大的系统,假若有一个资源不可用,可能会形成线程所有阻塞(hang)在这个资源上,形成整个系统不可用。降级机制在高并发系统中是很是广泛的:好比推荐服务中,若是个性化推荐服务不可用,能够降级补充热点数据,不至于形成前端页面是开天窗。 推荐一个Java依赖隔离工具Hystrix

3. 提早演练。 在项目上线前,演练缓存层宕掉后,应用以及后端的负载状况以及可能出现的问题,在此基础上作一些预案设定。

总结

Redis的学习到此为止。之后会总结一下Redis的面试篇。 接下来会学习netty相关知识,敬请期待。

最后

若是对 Java、大数据感兴趣请长按二维码关注一波,我会努力带给大家价值。以为对你哪怕有一丁点帮助的请帮忙点个赞或者转发哦。 关注公众号**【爱编码】,回复2019**有相关资料哦。

相关文章
相关标签/搜索