之前讲解了redis主从读写分离+sentinel架构,保证了slave节点能横向扩展提高qps,也保证了
99.99%的高可用性。但是也存在如下一些问题:
- 单master架构的内存瓶颈问题
- master的内存大小的上限,就是整个集群数据量的上限
- 用之前的方式配置读写写分离,最大的问题是master和slave存的内容完全一样, 比较浪费内存资源。
所以做好能够突破单master的瓶颈,搭建多个master,承载海量数据,一个master对几个slave,这样就能横向扩展。
reids官网提供了另一种集群方式,就是redis cluster,能满足上面的需求。
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算法 + 虚拟节点(自动负载均衡)
- 搞一个hash环,然后把多个master节点分配在hash环上 , 当对key进行hash,落在hash环上,然后顺时针移动,碰到的第一个master节点就存。
- 当一个master节点挂了,只是在之前的master节点上找不到了,会顺时针去下一个节点,也找不到。但是对另外两台机器上有的数据没影响,就是导致1/3的数据涌入数据库。重新查询一次。
- 缓存热点问题
- 可能集中在某个hash区间内的值特别多,然后导致大量的数据全部涌入同一个masterneural,造成master的热点问题,性能出现瓶颈。
- 解决缓存热点
- 会搞一堆虚拟节点,例如1号机器有3个点虚拟节点,3个机器就有9个节点,交叉均匀分布在环上,那么,在某个区间内的数据,又会分不到不同的节点内。
- hash slot算法(redis-cluster使用)
- hash算法是对机器数量取模,当机器挂了一个后,数量就变少了,那么所有的取模都变了,导致所有的key命中不准
- 而hash slot就是不对机器数取模,不管多少个机器,都对slot数量16384取模。
- 机器在或者不在都对16384取模,那么当一个机器挂了,只会影响该机器上的slot,也就是1/3的数据命中不到,去数据库取数据。
- hash slot有16384的节点,那么能尽量保证,数据命中的数据均匀。
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 7001
,cluster-announce-port 7001
,cluster-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
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在哪个节点上
- redis-cli -c,自动重定向
- 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的功能
- 1、判断节点宕机
5、总结
redis写了好几篇,主要是单机redis,主从redis,哨兵,然后加上cluster,大家可以根据自己的
项目灵活选择哪种架构方式,但是注意线上redis一定要做好数据安全,做好数据备份。然后可以使用
redis-benchmark
工具对redis进行压测,分析当前性能是否够用。