高可用Redis:Redis Cluster

 转(https://www.cnblogs.com/renpingsheng/p/9862485.html)

Redis Cluster是Redis官方提供的Redis集群功能php

1.为何要实现Redis Cluster

1.主从复制不能实现高可用
2.随着公司发展,用户数量增多,并发愈来愈多,业务须要更高的QPS,而主从复制中单机的QPS可能没法知足业务需求
3.数据量的考虑,现有服务器内存不能知足业务数据的须要时,单纯向服务器添加内存不能达到要求,此时须要考虑分布式需求,把数据分布到不一样服务器上
4.网络流量需求:业务的流量已经超过服务器的网卡的上限值,能够考虑使用分布式来进行分流
5.离线计算,须要中间环节缓冲等别的需求

2.数据分布

2.1 为何要作数据分布

全量数据,单机Redis节点没法知足要求,按照分区规则把数据分到若干个子集当中css

2.2 经常使用数据分布方式之顺序分布

好比:1到100个数字,要保存在3个节点上,按照顺序分区,把数据平均分配三个节点上
1号到33号数据保存到节点1上,34号到66号数据保存到节点2上,67号到100号数据保存到节点3上

顺序分区经常使用在关系型数据库的设计html

2.3 经常使用数据分布方式之哈希分布

例如1到100个数字,对每一个数字进行哈希运算,而后对每一个数的哈希结果除以节点数进行取余,余数为1则保存在第1个节点上,余数为2则保存在第2个节点上,余数为0则保存在第3个节点,这样能够保证数据被打散,同时保证数据分布的比较均匀

哈希分布方式分为三个分区方式:node

2.3.1 节点取余分区

好比有100个数据,对每一个数据进行hash运算以后,与节点数进行取余运算,根据余数不一样保存在不一样的节点上python

节点取余方式是很是简单的一种分区方式mysql

节点取余分区方式有一个问题:即当增长或减小节点时,原来节点中的80%的数据会进行迁移操做,对全部数据从新进行分布linux

节点取余分区方式建议使用多倍扩容的方式,例如之前用3个节点保存数据,扩容为比之前多一倍的节点即6个节点来保存数据,这样只须要适移50%的数据。数据迁移以后,第一次没法从缓存中读取数据,必须先从数据库中读取数据,而后回写到缓存中,而后才能从缓存中读取迁移以后的数据redis

节点取余方式优势:算法

客户端分片
配置简单:对数据进行哈希,而后取余

节点取余方式缺点:sql

数据节点伸缩时,致使数据迁移
迁移数量和添加节点数据有关,建议翻倍扩容

2.3.2 一致性哈希分区

一致性哈希原理:

将全部的数据当作一个token环,token环中的数据范围是0到2的32次方。而后为每个数据节点分配一个token范围值,这个节点就负责保存这个范围内的数据。

对每个key进行hash运算,被哈希后的结果在哪一个token的范围内,则按顺时针去找最近的节点,这个key将会被保存在这个节点上。

在上面的图中,有4个key被hash以后的值在在n1节点和n2节点之间,按照顺时针规则,这4个key都会被保存在n2节点上, 若是在n1节点和n2节点之间添加n5节点,当下次有key被hash以后的值在n1节点和n5节点之间,这些key就会被保存在n5节点上面了 在上面的例子里,添加n5节点以后,数据迁移会在n1节点和n2节点之间进行,n3节点和n4节点不受影响,数据迁移范围被缩小不少 同理,若是有1000个节点,此时添加一个节点,受影响的节点范围最多只有千分之2 一致性哈希通常用在节点比较多的时候

一致性哈希分区优势:

采用客户端分片方式:哈希 + 顺时针(优化取余)
节点伸缩时,只影响邻近节点,可是仍是有数据迁移

一致性哈希分区缺点:

翻倍伸缩,保证最小迁移数据和负载均衡

2.3.3 虚拟槽分区

虚拟槽分区是Redis Cluster采用的分区方式

预设虚拟槽,每一个槽就至关于一个数字,有必定范围。每一个槽映射一个数据子集,通常比节点数大

Redis Cluster中预设虚拟槽的范围为0到16383

步骤:

1.把16384槽按照节点数量进行平均分配,由节点进行管理
2.对每一个key按照CRC16规则进行hash运算 3.把hash结果对16383进行取余 4.把余数发送给Redis节点 5.节点接收到数据,验证是否在本身管理的槽编号的范围 若是在本身管理的槽编号范围内,则把数据保存到数据槽中,而后返回执行结果 若是在本身管理的槽编号范围外,则会把数据发送给正确的节点,由正确的节点来把数据保存在对应的槽中

须要注意的是:Redis Cluster的节点之间会共享消息,每一个节点都会知道是哪一个节点负责哪一个范围内的数据槽

虚拟槽分布方式中,因为每一个节点管理一部分数据槽,数据保存到数据槽中。当节点扩容或者缩容时,对数据槽进行从新分配迁移便可,数据不会丢失。
虚拟槽分区特色:

使用服务端管理节点,槽,数据:例如Redis Cluster
能够对数据打散,又能够保证数据分布均匀

2.3 顺序分布与哈希分布的对比

3.Redis Cluster基本架构

3.1 节点

Redis Cluster是分布式架构:即Redis Cluster中有多个节点,每一个节点都负责进行数据读写操做

每一个节点之间会进行通讯。

3.2 meet操做

节点之间会相互通讯

meet操做是节点之间完成相互通讯的基础,meet操做有必定的频率和规则

3.3 分配槽

把16384个槽平均分配给节点进行管理,每一个节点只能对本身负责的槽进行读写操做

因为每一个节点之间都彼此通讯,每一个节点都知道另外节点负责管理的槽范围

客户端访问任意节点时,对数据key按照CRC16规则进行hash运算,而后对运算结果对16383进行取做,若是余数在当前访问的节点管理的槽范围内,则直接返回对应的数据
若是不在当前节点负责管理的槽范围内,则会告诉客户端去哪一个节点获取数据,由客户端去正确的节点获取数据

3.4 复制

保证高可用,每一个主节点都有一个从节点,当主节点故障,Cluster会按照规则实现主备的高可用性

对于节点来讲,有一个配置项:cluster-enabled,便是否以集群模式启动

3.5 客户端路由

3.5.1 moved重定向

1.每一个节点经过通讯都会共享Redis Cluster中槽和集群中对应节点的关系
2.客户端向Redis Cluster的任意节点发送命令,接收命令的节点会根据CRC16规则进行hash运算与16383取余,计算本身的槽和对应节点 3.若是保存数据的槽被分配给当前节点,则去槽中执行命令,并把命令执行结果返回给客户端 4.若是保存数据的槽不在当前节点的管理范围内,则向客户端返回moved重定向异常 5.客户端接收到节点返回的结果,若是是moved异常,则从moved异常中获取目标节点的信息 6.客户端向目标节点发送命令,获取命令执行结果 

须要注意的是:客户端不会自动找到目标节点执行命令

槽命中:直接返回

[root@mysql ~]# redis-cli -p 9002 cluster keyslot hello (integer) 866

槽不命中:moved异常

[root@mysql ~]# redis-cli -p 9002 cluster keyslot php (integer) 9244

[root@mysql ~]# redis-cli -c -p 9002 127.0.0.1:9002> cluster keyslot hello (integer) 866 127.0.0.1:9002> set hello world -> Redirected to slot [866] located at 192.168.81.100:9003 OK 192.168.81.100:9003> cluster keyslot python (integer) 7252 192.168.81.100:9003> set python best -> Redirected to slot [7252] located at 192.168.81.101:9002 OK 192.168.81.101:9002> get python "best" 192.168.81.101:9002> get hello -> Redirected to slot [866] located at 192.168.81.100:9003 "world" 192.168.81.100:9003> exit [root@mysql ~]# redis-cli -p 9002 127.0.0.1:9002> cluster keyslot python (integer) 7252 127.0.0.1:9002> set python best OK 127.0.0.1:9002> set hello world (error) MOVED 866 192.168.81.100:9003 127.0.0.1:9002> exit [root@mysql ~]# 

3.5.2 ask重定向

在对集群进行扩容和缩容时,须要对槽及槽中数据进行迁移

当客户端向某个节点发送命令,节点向客户端返回moved异常,告诉客户端数据对应的槽的节点信息

若是此时正在进行集群扩展或者缩空操做,当客户端向正确的节点发送命令时,槽及槽中数据已经被迁移到别的节点了,就会返回ask,这就是ask重定向机制

步骤:

1.客户端向目标节点发送命令,目标节点中的槽已经迁移支别的节点上了,此时目标节点会返回ask转向给客户端
2.客户端向新的节点发送Asking命令给新的节点,而后再次向新节点发送命令
3.新节点执行命令,把命令执行结果返回给客户端

moved异常与ask异常的相同点和不一样点

二者都是客户端重定向
moved异常:槽已经肯定迁移,即槽已经不在当前节点
ask异常:槽还在迁移中

3.5.3 smart智能客户端

使用智能客户端的首要目标:追求性能

从集群中选一个可运行节点,使用Cluster slots初始化槽和节点映射

将Cluster slots的结果映射在本地,为每一个节点建立JedisPool,至关于为每一个redis节点都设置一个JedisPool,而后就能够进行数据读写操做

读写数据时的注意事项:

每一个JedisPool中缓存了slot和节点node的关系
key和slot的关系:对key进行CRC16规则进行hash后与16383取余获得的结果就是槽 JedisCluster启动时,已经知道key,slot和node之间的关系,能够找到目标节点 JedisCluster对目标节点发送命令,目标节点直接响应给JedisCluster 若是JedisCluster与目标节点链接出错,则JedisCluster会知道链接的节点是一个错误的节点 此时JedisCluster会随机节点发送命令,随机节点返回moved异常给JedisCluster JedisCluster会从新初始化slot与node节点的缓存关系,而后向新的目标节点发送命令,目标命令执行命令并向JedisCluster响应 若是命令发送次数超过5次,则抛出异常"Too many cluster redirection!" 

3.6 多节点命令实现

Redis Cluster不支持使用scan命令扫描全部节点
多节点命令就是在在全部节点上都执行一条命令
批量操做优化

3.6.1 串行mget

定义for循环,遍历全部的key,分别去全部的Redis节点中获取值并进行汇总,简单,可是效率不高,须要n次网络时间

3.6.2 串行IO

对串行mget进行优化,在客户端本地作内聚,对每一个key进行CRC16hash,而后与16383取余,就能够知道哪一个key对应的是哪一个槽

本地已经缓存了槽与节点的对应关系,而后对key按节点进行分组,成立子集,而后使用pipeline把命令发送到对应的node,须要nodes次网络时间,大大减小了网络时间开销

3.6.3 并行IO

并行IO是对串行IO的一个优化,把key分组以后,根据节点数量启动对应的线程数,根据多线程模式并行向node节点请求数据,只须要1次网络时间

3.6.4 hash_tag

将key进行hash_tag的包装,而后把tag用大括号括起来,保证全部的key只向一个node请求数据,这样执行相似mget命令只须要去一个节点获取数据便可,效率更高

3.6.5 四种优化方案优缺点分析

3.7 故障发现

Redis Cluster经过ping/pong消息实现故障发现:不须要sentinel

ping/pong不只能传递节点与槽的对应消息,也能传递其余状态,好比:节点主从状态,节点故障等

故障发现就是经过这种模式来实现,分为主观下线和客观下线

3.7.1 主观下线

某个节点认为另外一个节点不可用,'偏见',只表明一个节点对另外一个节点的判断,不表明全部节点的认知

主观下线流程:

1.节点1按期发送ping消息给节点2 2.若是发送成功,表明节点2正常运行,节点2会响应PONG消息给节点1,节点1更新与节点2的最后通讯时间 3.若是发送失败,则节点1与节点2之间的通讯异常判断链接,在下一个定时任务周期时,仍然会与节点2发送ping消息 4.若是节点1发现与节点2最后通讯时间超过node-timeout,则把节点2标识为pfail状态 

3.7.2 客观下线

当半数以上持有槽的主节点都标记某节点主观下线时,能够保证判断的公平性

集群模式下,只有主节点(master)才有读写权限和集群槽的维护权限,从节点(slave)只有复制的权限

客观下线流程:

1.某个节点接收到其余节点发送的ping消息,若是接收到的ping消息中包含了其余pfail节点,这个节点会将主观下线的消息内容添加到自身的故障列表中,故障列表中包含了当前节点接收到的每个节点对其余节点的状态信息 2.当前节点把主观下线的消息内容添加到自身的故障列表以后,会尝试对故障节点进行客观下线操做

故障列表的周期为:集群的node-timeout * 2,保证之前的故障消息不会对周期内的故障消息形成影响,保证客观下线的公平性和有效性

3.8 故障恢复

3.8.1 资格检查

对从节点的资格进行检查,只有难过检查的从节点才能够开始进行故障恢复
每一个从节点检查与故障主节点的断线时间
超过cluster-node-timeout * cluster-slave-validity-factor数字,则取消资格
cluster-node-timeout默认为15秒,cluster-slave-validity-factor默认值为10
若是这两个参数都使用默认值,则每一个节点都检查与故障主节点的断线时间,若是超过150秒,则这个节点就没有成为替换主节点的可能性

3.9.2 准备选举时间

使偏移量最大的从节点具有优先级成为主节点的条件

3.8.3 选举投票

对选举出来的多个从节点进行投票,选出新的主节点

3.8.4 替换主节点

当前从节点取消复制变成离节点(slaveof no one)
执行cluster del slot撤销故障主节点负责的槽,并执行cluster add slot把这些槽分配给本身 向集群广播本身的pong消息,代表已经替换了故障从节点

3.8.5 故障转移演练

对某一个主节点执行kill -9 {pid}来模拟宕机的状况

3.9 Redis Cluster的缺点

当节点数量不少时,性能不会很高
解决方式:使用智能客户端。智能客户端知道由哪一个节点负责管理哪一个槽,并且当节点与槽的映射关系发生改变时,客户端也会知道这个改变,这是一种很是高效的方式

4.搭建Redis Cluster

搭建Redis Cluster有两种安装方式

cluster-require-full-coverage默认为yes,便是否集群中的全部节点都是在线状态且16384个槽都处于服务状态时,集群才会提供服务

集群中16384个槽所有处于服务状态,保证集群完整性

当某个节点故障或者正在故障转移时获取数据会提示:(error)CLUSTERDOWN The cluster is down

建议把cluster-require-full-coverage设置为no

5.2 带宽消耗

Redis Cluster节点之间会按期交换Gossip消息,以及作一些心跳检测

官方建议Redis Cluster节点数量不要超过1000个,当集群中节点数量过多时,会产生不容忽视的带宽消耗

消息发送频率:节点发现与其余节点最后通讯时间超过cluster-node-timeout /2时,会直接发送PING消息

消息数据量:slots槽数组(2kb空间)和整个集群1/10的状态数据(10个节点状态数据约为1kb)

节点部署的机器规模:集群分布的机器越多且每台机器划分的节点数越均匀,则集群内总体的可用带宽越高

带宽优化:

避免使用'大'集群:避免多业务使用一个集群,大业务能够多集群 cluster-node-timeout:带宽和故障转移速度的均衡 尽可能均匀分配到多机器上:保证高可用和带宽

5.3 Pub/Sub广播

在任意一个cluster节点执行publish,则发布的消息会在集群中传播,集群中的其余节点都会订阅到消息,这样节点的带宽的开销会很大

publish在集群每一个节点广播,加剧带宽

解决办法:须要使用Pub/Sub时,为了保证高可用,能够单独开启一套Redis Sentinel

5.4 集群倾斜

对于分布式数据库来讲,存在倾斜问题是比较常见的

集群倾斜也就是各个节点使用的内存不一致

5.4.1 数据倾斜缘由

1.节点和槽分配不均,若是使用redis-trib.rb工具构建集群,则出现这种状况的机会很少

redis-trib.rb info ip:port查看节点,槽,键值分布 redis-trib.rb rebalance ip:port进行均衡(谨慎使用)

2.不一样槽对应键值数量差别比较大

CRC16算法正常状况下比较均匀
可能存在hash_tag
cluster countkeysinslot {slot}获取槽对应键值个数

3.包含bigkey:例如大字符串,几百万的元素的hash,set等

在从节点:redis-cli --bigkeys 优化:优化数据结构

4.内存相关配置不一致

hash-max-ziplist-value:知足必定条件状况下,hash可使用ziplist set-max-intset-entries:知足必定条件状况下,set可使用intset 在一个集群内有若干个节点,当其中一些节点配置上面两项优化,另一部分节点没有配置上面两项优化 当集群中保存hash或者set时,就会形成节点数据不均匀 优化:按期检查配置一致性

5.请求倾斜:热点key

重要的key或者bigkey Redis Cluster某个节点有一个很是重要的key,就会存在热点问题

5.4.2 集群倾斜优化:

避免bigkey 热键不要用hash_tag 当一致性不高时,能够用本地缓存+ MQ(消息队列)

5.5 集群读写分离

只读链接:集群模式下,从节点不接受任何读写请求

当向从节点执行读请求时,重定向到负责槽的主节点

readonly命令能够读:链接级别命令,当链接断开以后,须要再次执行readonly命令

读写分离:

一样的问题:复制延迟,读取过时数据,从节点故障
修改客户端:cluster slaves {nodeId}

5.6 数据迁移

官方迁移工具:redis-trib.rb和import

只能从单机迁移到集群

不支持在线迁移:source须要停写

不支持断点续传

单线程迁移:影响深度

在线迁移:

惟品会:redis-migrate-tool
豌豆荚:redis-port

5.7 集群VS单机

集群的限制:

key批量操做支持有限:例如mget,mset必须在一个slot key事务和Lua支持有限:操做的key必须在一个节点 key是数据分区的最小粒度:不支持bigkey分区 不支持多个数据库:集群模式下只有一个db0 复制只支持一层:不支持树形复制结构 Redis Cluster知足容量和性能的扩展性,不少业务'不须要' 大多数时客户端性能会'下降' 命令没法跨节点使用:mget,keys,scan,flush,sinter等 Lua和事务没法跨节点使用 客户端维护更复杂:SDK和应用自己消耗(例如更多的链接池)

不少场景Redis Sentinel已经够用了

6.Redis Cluster总结:

1.Redis Cluster数据分区规则采用虚拟槽方式(16384个槽),每一个节点负责一部分槽和相关数据,实现数据和请求的负载均衡 2.搭建Redis Cluster划分四个步骤:准备节点,meet操做,分配槽,复制数据。 3.Redis官方推荐使用redis-trib.rb工具快速搭建Redis Cluster 4.集群伸缩经过在节点之间移动槽和相关数据实现 扩容时根据槽迁移计划把槽从源节点迁移到新节点 收缩时若是下线的节点有负责的槽须要迁移到其余节点,再经过cluster forget命令让集群内全部节点忘记被下线节点 5.使用smart客户端操做集群过到通讯效率最大化,客户端内部负责计算维护键,槽以及节点的映射,用于快速定位到目标节点 6.集群自动故障转移过程分为故障发现和节点恢复。节点下线分为主观下线和客观下线,当超过半数节点认为故障节点为主观下线时,标记这个节点为客观下线状态。从节点负责对客观下线的主节点触发故障恢复流程,保证集群的可用性 7.开发运维常见问题包括:超大规模集群带席消耗,pub/sub广播问题,集群倾斜问题,单机和集群对比等
 
标签:  rediscluster集群slotmeet
相关文章
相关标签/搜索