Redis集群

2019/04/06 分布式

Redis集群

redis有三种集群策略:主从复制哨兵(sentinel)集群(Redis-Cluster)

1.主从复制

主从复制中,数据库分为两类,主数据库(master)和从数据库(slave)。

  • 主数据库可以进行读写操作,当读写操作导致数据变化时会自动将数据同步给从数据库;
  • 从数据库都是只读的,并且接收主数据库同步过来的数据;
  • 一个master可以拥有多个slave,但是一个slave只能对应一个master。

1.1 主从数据库配置

master不用配置,slave的conf文件加入slaveof 主ip 主port

或者slave启动时,启动命令redis-server --port 从port --slaveof 主ip 主port

slave一般是只读的,可以改为可写,但是写入的数据很容易被master同步没,所以默认为只读。

info replication命令查看主从信息。

1.2 主从复制工作机制

当slave启动后,主动向master发送SYNC命令。master接收到SYNC命令后在后台保存快照(RDB持久化)和缓存保存快照这段时间的命令,然后将保存的快照文件和缓存的命令发送给slave。slave接收到快照文件和命令后加载快照文件和缓存的执行命令。

复制初始化后,master每次接收到的写命令都会异步发送给slave,保证主从数据一致性。

2.哨兵

Redis2.8提供哨兵工具实现自动化的系统监控和故障恢复功能。

2.1 哨兵的作用

  • 监控master,slave是否正常运行;
  • master出现故障自动将slave转换为master;
  • 多哨兵配置时,哨兵之间也会自动监控;
  • 多个哨兵可以监控一个redis

2.2 哨兵的配置

哨兵的配置文件sertinel.conf,哨兵进程启动时会读取该文件的内容,通过sentinel monitor master -主name 主ip 主port quorum命令进行配置。

  • quorum:法定人数,指定一个数目,当哨兵监控到master下线时,哨兵会向其他哨兵节点发送命令询问他们是否也认为master下线了,如果达到该数目,则确认master已经下线;
  • 配置哨兵监控一个系统时,只需要配置其监控master即可,会自动发现所有复制该master的slave;
  • 一个哨兵可以监控多个master,只需要提供多个该配置项即可;

2.3哨兵工作机制

哨兵启动后,会与要监控的master建立两条连接:

  • 一条订阅master的_sentinal_:hello频道,以获取其他监控该master的哨兵的节点信息;
  • 另一条连接定期向master发送INFO等命令获取master本身的信息。

与master建立连接后,哨兵会执行三个操作(这三个操作的发送频率都可以在配置文件中配置):

  • 定期向master和slave发送INFO命令,目的是获取当前数据库的相关信息从而实现新节点的自动发现。所以可以自动发现slave的信息;
  • 定期向master和slave的_sentinel_:hello频道发送自己的信息,目的是与同样监控这些数据库的哨兵共享自己的信息,其他哨兵可以通过该信息判断发送者是否是新发现的哨兵,是的话会创建一个到该哨兵的连接用于发送PING命令;
  • 定期向master,slave和其他哨兵发送PING命令,目的是监控这些数据库和节点有没有停止服务,如果被PING的master超时未回复,该哨兵则认为其下线了,然后向其他哨兵发送命令询问他们是否也认为该master下线,如果达到quorum的值,就确认该master已经下线,并选举领头的哨兵节点对系统发起故障恢复。

哨兵确认master下线后,故障恢复的操作需要由选举的领头哨兵执行,选举采用Raft算法

  • 发现master下线的哨兵节点(节点A)向每个哨兵发送命令,要求对方选自己为领头哨兵;
  • 如果目标哨兵节点没有选过其他的节点,则会同意选举A为领头哨兵;
  • 如果有超过一半的哨兵同意选举A为领头节点,则A当选;
  • 如果有多个哨兵节点同时参选领头节点,此时有可能存在一轮投票无竞选者胜出,每个参选节点等待一个随机时间后再次发起参选请求,进行下一轮投票,直到选举出领头哨兵。

选出领头哨兵后,领头者开始进行故障恢复,从出现故障的master的slave中选出一个当选新的master。选择规则如下:

  • 所有在线的slave中选择优先级最高的,通过slave-priority设置;
  • 如果有多个最高优先级的slave,则选取复制偏移量最大(复制越完整)的当选;
  • 如果以上条件都一样,选取id最小的slave。

挑选出需要继任的slave后,领头哨兵向该slave发送命令使其升级为master,然后再向其他slave发送命令接受新的master,最后更新数据。

旧的master恢复服务后,更新为新的master的slave继续运行。

3.集群(Redis-Cluster,官方推荐)

即使使用哨兵,rendis每个实例也是全量存储,每个redis存储的内容都是完整的数据,浪费内存且有木桶效应。为了最大化利用内存,采用redis-cluster进行分片存储,也就是分布式存储。

3.1 结构特点

Redis-Cluster采用去中心化结构,每个节点保存数据和整个集群状态,每个节点都和其他节点连接,其结构特点:

  • 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议化传输;
  • 节点的fail是通过集群中超过半数的节点检测失效时才生效;
  • 客户端与redis节点直连,不需要中间proxy层,客户端不需要连接集群的所有节点,连接任意一个可用节点即可;
  • redis-cluster把所有的物理节点映射到[0-16383]slot(槽)上(不一定是平均分配),cluster负责维护node<->slot<->value。
  • redis集群预分好16384个槽,当需要在redis集群中放置一个key-value时,根据hash_slot = crc16(key) mod 16384算出hash值,决定将这个key放到哪个slot中。

3.2 Redis-Cluster的主从模式

和前面介绍的主从模式类似,一个主节点对应多个从节点,主节点提供数据读写,从节点只读进行数据备份。

不同的是,Redis-Cluster中的slot只分布在主节点中,数据写入的时候,在这些主节点的slot中写入。

3.3 Redis-Cluster构建

至少需要三主三从,且每个实例使用不同的配置文件,主从不用

配置,集群自己选择。

  • 修改每个实例的conf文件:
    • cluster-enabled yes –表示开启集群;
    • cluster-config-file node-6380.conf –集群配置文件名,每个实例配置的要不同,redis根据文件名自动新建。
  • 用集群工具创建集群:
    • 安装ruby;
    • 启动6个redis实例,每个实例的集群都打开:redis-service ../6380/redis.conf;
    • 将节点加入到集群中:redis-trib.rb create --replicas 1 192.168.0.1:6380 192.168.0.1:6381 192.168.0.1:6382 192.168.0.1:6383 192.168.0.1:6384 192.168.0.1:6385,之后可通过info cluster查看;
    • 客户端连接,连接任意一个redis实例即可:redis-cli -c -p 6380

4.集群中可能出现的问题

4.1 缓存雪崩

  • 含义:对于系统A ,假设每天高峰期每秒5000个请求,本来缓缓从在高峰期可以扛住每秒4000个请求,但是缓存机器出现意外全盘宕机。缓存挂了,此时每秒5000个请求全部落到数据库,数据库扛不住也就挂了。即使重启数据库也立马会被新的流量打死,这就是缓存雪崩。

  • 解决方案:

    • 事前:redis高可用,主从+哨兵,redis cluster,避免全盘崩溃;

    • 事中:本地ehcache缓存+hystrix限流/降级,避免MySQL被打死;

      系统收到请求后,先查本地ehcache缓存,如果没有查到再查redis,如果都没有,再查数据库,将数据库查到的结果,写入ehcache和redis中;

      限流组件,可以设置每秒的请求有多少能通过组件,剩余的未通过的请求走降级方案,返回默认值或友情提示。

    • 事后:redis持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

4.2 缓存穿透

  • 含义:假设每秒有5000个请求,但是其中4000个请求在缓存中查不到,数据库中也查不到(比如数据库id从1开始,但是黑客发来的请求id都是负数)。这就是缓存穿透,会直接把数据库打死。
  • 解决方案:每次从数据库中只要没查到,就写一个空值到缓存中,比如set -999 UNKNOWN,然后设置一个过期时间。这样下次有相同的key来访问的时候,在缓存失效前,都可以直接从缓存中取到数据。

4.3 缓存击穿

  • 含义:某个key非常热点,访问很频繁,处于集中式高并发访问的情况,当这个key在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库。

  • 解决方案:

    • 将热点数据设置为永远不过期;

    • 基于redis/zookeeper实现互斥锁,等待一个请求构建完缓存之后,再释放锁,进而其他请求才能通过该key访问数据。

参考:

关于redis的主从、哨兵、集群

redis:详解三种集群策略

Redis Cluster集群

Search

    Table of Contents