高并发redis - cluster

之前讲解了redis主从读写分离+sentinel架构,保证了slave节点能横向扩展提高qps,也保证了
99.99%的高可用性。但是也存在如下一些问题:

  • 单master架构的内存瓶颈问题
    • master的内存大小的上限,就是整个集群数据量的上限
    • 用之前的方式配置读写写分离,最大的问题是master和slave存的内容完全一样, 比较浪费内存资源。

所以做好能够突破单master的瓶颈,搭建多个master,承载海量数据,一个master对几个slave,这样就能横向扩展。

reids官网提供了另一种集群方式,就是redis cluster,能满足上面的需求。


redis如何通过master横向扩容支撑1T+数据量.png

redis cluster介绍

  • 自动将数据进行分片,每个master上放一部分数据
  • 提供内置的高可用支持。部分master不可用时,还是可以继续工作
  • 在 redis cluster 架构下,每个redis要开放两个端口号,一个是6379,另一个加10000,16379端口号是用来进行节点通信的,也就是cluster bus的东西,集群总线。cluster bus的通信,用来进行故障检测,配置更新,故障转移授权。
  • cluster bus用了另外一种二进制的协议,主要用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间
  • redis cluster = sentinel+sharded (哨兵+多master数据共享)

redis cluster特性

  • 支撑N个redis master node ,每个master node都可以挂在多个slave node
  • 读写分离架构,对于每个master来说,写就写到master ,然后读就从master对应的slave读。
  • 因为每个master都有slave节点,如果master挂掉,redis cluster这套机制,就会将某个slave切换成master
  • redis cluster(多master + 高可用)

所以到这里已经有单机redis,主从复制架构,cluster架构,面对这么多架构,我们如何做选择?

  • 如果数据量很少,主要是承载高并发高性能场景,比如缓存一般就几个g,单机足够了
  • replication, 一个master,多个slave,要几个slave跟你要求的吞吐量有关系,然后自己搭建一个sentinal集群,保证redis主从架构的高可用性,就可以了。
  • redis cluster,主要是针对海量数据,高并发,高可用的场景,海量数据,如果数量很大,那么建议使用。

下面就从一下几个方面介绍redis-cluster

  • 1、多master下,不同key分布在不同master的各种算法原理
  • 2、实验:使用docker搭建一套6节点,3主3从的redis-cluster集群(带redis配置和docker启动脚本)
  • 3、cluster横向扩展以及冗余节点
  • 4、redis-cluster的核心原理分析:gossip通信,jedis smart定位,主备切换
  • 5、总结

多master下,不同key分布在不同master的各种算法原理

由于有多个master,能写的地方不止一个,所以,每有一个写操作过来,都需要选择一个master去写,
所以需要有一个,算法,把每个key较为均匀的分配到各个master节点上,这样才能保证发挥每台机器的性能。
算法从以前到现在经历的以下几个版本。

  • 最原始的hash算法
    • 算法流程
      • 来了一个key,计算hash值,对master,对master节点数取模,取模就分配到对应的master节点
      • 例如3个master节点,就对3取模,然后值在0-2之间,0就分配给1号节点,1就分配给2号节点,2就分配给3号节点
    • 缺点
      • 3个节点,对3取模,样本数过少,很容易出现那种,大多数key都计算出一个结果,每个机器压力差别大。
      • 容错率太低,当一个master挂了,那么会导致之前的所有key不可用
        • 假设之前有三个节点,一个key之前计算出来为123对3取模是0,存0号机器
        • 但是挂了一个,当这个key又过来123对2取模(挂了一个3变2)为1,存2号机器。
        • 所以之前存的key,当挂了一个机器以后就都不可用了。
最老土的hash算法以及弊端.png
  • 一致性hash算法 + 虚拟节点(自动负载均衡)
    • 搞一个hash环,然后把多个master节点分配在hash环上 , 当对key进行hash,落在hash环上,然后顺时针移动,碰到的第一个master节点就存。
    • 当一个master节点挂了,只是在之前的master节点上找不到了,会顺时针去下一个节点,也找不到。但是对另外两台机器上有的数据没影响,就是导致1/3的数据涌入数据库。重新查询一次。
    • 缓存热点问题
      • 可能集中在某个hash区间内的值特别多,然后导致大量的数据全部涌入同一个masterneural,造成master的热点问题,性能出现瓶颈。
    • 解决缓存热点
      • 会搞一堆虚拟节点,例如1号机器有3个点虚拟节点,3个机器就有9个节点,交叉均匀分布在环上,那么,在某个区间内的数据,又会分不到不同的节点内。
一致性hash算法的讲解和优点.png

一致性hash算法的虚拟节点实现负载均衡.png
  • hash slot算法(redis-cluster使用)
    • hash算法是对机器数量取模,当机器挂了一个后,数量就变少了,那么所有的取模都变了,导致所有的key命中不准
    • 而hash slot就是不对机器数取模,不管多少个机器,都对slot数量16384取模。
    • 机器在或者不在都对16384取模,那么当一个机器挂了,只会影响该机器上的slot,也就是1/3的数据命中不到,去数据库取数据。
    • hash slot有16384的节点,那么能尽量保证,数据命中的数据均匀。
redis cluster hash slot算法.png

2、实验:使用docker搭建一套6节点,3主3从的redis-cluster集群(带redis配置和docker启动脚本)

redis.conf

port 7001
requirepass "123456"
dir "/data"
logfile "redis.log"
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 192.168.5.10
cluster-announce-port 7001
cluster-announce-bus-port 17001
masterauth "123456"
appendonly yes

docker启动一个redis脚本

docker run -d --name redis7001 -p 7001:7001 -p 17001:17001 -v /data/redis-cluster/7001:/data redis redis-server /data/redis.conf

创建集群脚本

redis-cli -c -h  192.168.5.10 -p 7001 -a 123456 --cluster create 192.168.5.10:7001 192.168.5.10:7002 192.168.5.11:7003 192.168.5.11:7004 192.168.5.12:7005 192.168.5.12:7006 --cluster-replicas 1

步骤:

  • 1、准备三台虚拟机,然后创建6个 /data/cluster-redis/7001文件夹,每台机器两个,使用7001-7006六个数字标识
  • 2、在每个 /data/cluster-redis/700*下面新建一个 redis.conf, 把上面的redis.conf配置复制进去
    • 注意修改 redis.conf 中的 cluster-announce-ip 为本机ip
    • 注意修改 port 7001cluster-announce-port 7001cluster-announce-bus-port 17001,这三个配置,例如是7001目录下就都是7001,7002目录下就都是7002
  • 3、每天机器启动两个redis实例,使用上面的docker脚本
    • 注意映射端口和挂载盘的时候,所有的700*都要对应修改。
  • 4、6个实例启动完毕,进入redis7001这个容器去创建集群
    • 1.docker exec -it redis7001 /bin/bash
    • 2.然后使用上面创建集群脚本,注意修改ip为三台机器的ip
  • 5、这样,就创建好了三主三从的cluster集群。
  • 6、验证集群创建好了没,就进入redis7001节点, 然后进入redis,然后执行 info replication
    • docker exec -it redis7001 /bin/bash
    • redis-cli -h 192.168.5.10 -p 7001 -a 123456
    • info replication
    • 如下:就是一个master节点,然后挂了一个slave
redis cluster hash slot算法.png

3、cluster横向扩展以及冗余节点

redis_cluster通过master水平扩容来支撑更高的吞吐量+海量数据

  • redis cluster模式下,不建议做物理的读写分离了
  • 我们建议通过master的水平扩容,来横向扩展读写吞吐量,还有支撑更多的海量数据
  • redis单机,读吞吐是5w/s,写吞吐2w/s
  • 扩展redis更多master,那么如果有5台master,不就读吞吐可以达到总量25/s QPS,写可以达到10w/s QPS
  • redis单机不宜存过多的数据,内存,6G,8G,fork类操作的时候很耗时,会导致请求延时的问题
  • 扩容到5台master,能支撑的总的缓存数据量就是30G,40G -> 100台,600G,800G,甚至1T+,海量数据

冗余slave

  • 假设现在有3个master,有5个slave,就会出现有的master不止一个的情况
  • 这种情况是冗余slave,保证cluster的高可用性
  • 为了避免的场景,就是说,如果你每个master只有一个slave,万一说一个slave死了,然后很快,master也死了,那可用性还是降低了
  • 如果有的master挂了,那么slave能快速补成slave,那这个新master就没有slave了,那么多的冗余slave就会补上

4、redis-cluster的核心原理分析:gossip通信,jedis smart定位,主备切换

redis自身是有一些元数据的,比如 hashslot 对应每个master节点的信息,master和slave的对应关系,故障信息等。
(1):基本通信原理

  • redis cluster节点间采取gossip协议进行通信

  • 集中元数据管理

    • 集中式元数据管理,底层基于zookeeper(分布式协调的中间件)的集群所有元数据维护
    • 所有节点信息由zookeeper集中维护,同步
  • goosip

    • 跟git一样,每个节点都持有一份元数据
    • 不同节点如果出现了元数据的变更以后,就不断将元数据发送给其他节点,让其他节点进行更新
  • 集中式优缺点

    • 好处:元数据的更新和读取,时效性非常好,一旦元数据出现变更,立即更新到集中式的存储,其他节点读取的时候就能感知到
    • 缺点:所有元数据更新压力全部集中在一个地方,可能会导致元数据的存储压力
  • goosip优缺点

    • 好处:元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上更新,有一点延时,降低压力
    • 缺点:元数据更新有延时,可能导致集群的一些操作会有些滞后
  • 10000端口

    • 每个节点需要相互交换信息,需要一个特定的端口,就是自己的服务端口号 + 10000 , 比如7001,节点间通信端口就是17001端口
    • 每个节点每隔一段时间都会往另外几个节点发送ping消息,同时其他节点接受到ping之后,返回pong。就是网络正常的
  • 交换信息

    • 故障信息,节点的增加和移除,hash slot 信息,等等。
  • gossip协议

    • gossip协议包含多种消息,包括ping,pong,meet,fail,等等
    • meet: 某个节点发送meet给新加入的节点,让新节点加入集群中,然后新节点就会开始与其他节点进行通信
    • ping: 每个节点都会频繁给其他节点发送ping,其中包含自己的状态还有自己维护的集群元数据,互相通过ping交换元数据,每个节点每秒都会频繁发送ping给其他的集群,ping,频繁的互相之间交换数据,互相进行元数据的更新
    • pong: 返回ping和meet,包含自己的状态和其他信息,也可以用于信息广播和更新
    • fail: 某个节点判断另一个节点fail之后,就发送fail给其他节点,通知其他节点,指定的节点宕机了
  • ping消息深入

    • ping很频繁,而且要携带一些元数据,所以可能会加重网络负担
    • 每个节点每秒会执行10次ping,每次会选择5个最久没有通信的其他节点
    • 当然如果发现某个节点通信延时达到了cluster_node_timeout / 2,那么立即发送ping,避免数据交换延时过长,落后的时间太长了
    • 比如说,两个节点之间都10分钟没有交换数据了,那么整个集群处于严重的元数据不一致的情况,就会有问题
    • 所以cluster_node_timeout可以调节,如果调节比较大,那么会降低发送的频率
    • 每次ping,一个是带上自己节点的信息,还有就是带上1/10其他节点的信息,发送出去,进行数据交换
    • 至少包含3个其他节点的信息,最多包含总节点-2个其他节点的信息

(2)面向集群的jedis内部实现原理

  • 开发,jedis,redis的java client客户端,redis cluster,jedis cluster api
  • jedis cluster api与redis cluster集群交互的一些基本原理
  • 当在cluster集群中get值的时候,经常会出现move 到其他机器,因为这个key不在这个机器
  • 1、基于重定向的客户端
    • redis-cli -c,自动重定向
      -(1)请求重定向
      • 客户端可能会挑选任意一个redis实例去发送命令,每个redis实例接收到命令,都会计算key对应的hash slot
      • 如果在本地就在本地处理,否则返回moved给客户端,让客户端进行重定向
      • cluster keyslot mykey,可以查看一个key对应的hash slot是什么
        -(2)计算hash slot
      • 计算hash slot的算法,就是根据key计算CRC16值,然后对16384取模,拿到对应的hash slot
      • 用hash tag可以手动指定key对应的slot,同一个hash tag下的key,都会在一个hash slot中,比如set mykey1:{100}和set mykey2:{100}
        -(3)hash slot查找
      • 节点间通过gossip协议进行数据交换,就知道每个hash slot在哪个节点上
  • 2、smart jedis
    • 基于重定向的客户端,很消耗网络IO,因为大部分情况下,可能都会出现一次请求重定向,才能找到正确的节点(经常需多move一次)
    • 所以大部分的客户端,比如java redis客户端,就是jedis,都是smart的
    • 本地维护一份hashslot -> node的映射表,缓存,大部分情况下,直接走本地缓存就可以找到hashslot -> node,不需要通过节点进行moved重定向
  • 3、JedisCluster的工作原理
    • 在JedisCluster初始化的时候,就会随机选择一个node,初始化hashslot -> node映射表,同时为每个节点创建一个JedisPool连接池
    • 每次基于JedisCluster执行操作,首先JedisCluster都会在本地计算key的hashslot,然后在本地映射表找到对应的节点
    • 如果那个node正好还是持有那个hashslot,那么就ok; 如果说进行了reshard这样的操作,可能hashslot已经不在那个node上了,就会返回moved
    • 如果JedisCluter API发现对应的节点返回moved,那么利用该节点的元数据,更新本地的hashslot -> node映射表缓存
    • 重复上面几个步骤,直到找到对应的节点,如果重试超过5次,那么就报错,JedisClusterMaxRedirectionException
    • jedis老版本,可能会出现在集群某个节点故障还没完成自动切换恢复时,频繁更新hash slot,频繁ping节点检查活跃,导致大量网络IO开销
    • jedis最新版本,对于这些过度的hash slot更新和ping,都进行了优化,避免了类似问题
  • 4、hashslot迁移和ask重定向
    • 如果hash slot正在迁移,那么会返回ask重定向给jedis
    • jedis接收到ask重定向之后,会重新定位到目标节点去执行,但是因为ask发生在hash slot迁移过程中,所以,JedisCluster API收到ask是不会更新hashslot本地缓存
    • 已经可以确定说,hashslot已经迁移完了,moved是会更新本地hashslot->node映射表缓存的

(3)高可用性和主备切换原理

  • redis cluster的高可用的原理,几乎跟哨兵是类似的
    • 1、判断节点宕机
      • 如果一个节点认为另外一个节点宕机,那么就是pfail,主观宕机
      • 如果多个节点都认为另外一个节点宕机了,那么就是fail,客观宕机,跟哨兵的原理几乎一样,sdown,odown
      • 在cluster-node-timeout内,某个节点一直没有返回pong,那么就被认为pfail
      • 如果一个节点认为某个节点pfail了,那么会在gossip ping消息中,ping给其他节点,如果超过半数的节点都认为pfail了,那么就会变成fail
    • 2、从节点过滤
      • 对宕机的master node,从其所有的slave node中,选择一个切换成master node
      • 检查每个slave node与master node断开连接的时间,如果超过了cluster-node-timeout * cluster-slave-validity-factor,那么就没有资格切换成master
      • 这个也是跟哨兵是一样的,从节点超时过滤的步骤
    • 3、从节点选举
      • 哨兵:对所有从节点进行排序,slave priority,offset,run id
      • 每个从节点,都根据自己对master复制数据的offset,来设置一个选举时间,offset越大(复制数据越多)的从节点,选举时间越靠前,优先进行选举
      • 所有的master node开始slave选举投票,给要进行选举的slave进行投票,如果大部分master node(N/2 + 1)都投票给了某个从节点,那么选举通过,那个从节点可以切换成master
      • 从节点执行主备切换,从节点切换为主节点
    • 4、与哨兵比较
      • 整个流程跟哨兵相比,非常类似,所以说,redis cluster功能强大,直接集成了replication和sentinal的功能

5、总结
redis写了好几篇,主要是单机redis,主从redis,哨兵,然后加上cluster,大家可以根据自己的
项目灵活选择哪种架构方式,但是注意线上redis一定要做好数据安全,做好数据备份。然后可以使用
redis-benchmark 工具对redis进行压测,分析当前性能是否够用。

你可能感兴趣的:(高并发redis - cluster)