Redis专题(八)——Redis高可用(集群篇)

Redis专题(八) ——Redis高可用(集群篇)

(原创内容,转载请注明来源,谢谢)

1、分片方式

当主从结构的每个库都存储全量数据,则导致该主从系统的最大存储量被最小存储的redis服务器限定,形成木桶效应。

因此可以对redis进行水平扩容。由于redis轻量级,因此可以预先分足够多数量的片,并在存储的时候客户端采用某一算法将数据平均分配到不同的redis中。当数据量小的时候,每个片占用的内存都不多;当数据量很大时,也只需要将部分redis迁移到其他服务器即可。

2、集群概述

分片方式维护成本高,从redis3.0开始,集群是更好的解决方案。集群拥有和单机一样的性能,网络分区后提供可访问性,提供主库故障恢复支持。

集群支持所有单机执行的命令,对于多键命令(如MGET),如果多键都在同一个集群节点则正常返回,否则报错。另外,集群只支持0号数据库,如果使用select选择数据库也会保存。

3、配置集群

1)将每个节点设置成可集群

将每个数据库配置文件的cluster-enabled开启(设置成yes)即可。每个集群至少要三个数据库才可能正常运行。

集群会将当前节点记录的集群状态持久化存储在指定文件中,默认是当前工作目录下的nodes.conf文件。但是,要求每个节点文件不同,否则会报错。因此,需要在开启节点前,设置节点的持久化路径,通过配置文件的命令cluster-config-file /路径/nodes.conf。

连接上任一集群,通过INFOcluster命令,可以判断该集群是否可用。返回值中如果cluster_enabled:1,则表示可用。

该配置生效后,表示该节点是一个独立的节点,还未加入集群。

2)redis-trib.rb

redis源码提供的一个文件,用于协助节点加入集群,由于是ruby写的,因此需要节点环境安装配置ruby,另外还需要gem包redis,通过gem install redis进行安装。

3)初始化集群

/redis-trib.rb的路径/redis-trib.rbcreate --replicas 1 ip1:port1 ip2:port2 ip3:port3 ip4:port4 ip5:port5 ip6:port6

其中--replicas1表示集群中每个主库一个从库,该命令会将ip1、2、3的库设置为主库,4、5、6设置为从库。

也可以同一个ip的不同port加入集群。

4)集群创建的详细过程

a.redis-trib.rb以客户端形式连接所有节点,发送ping命令,如果有任何节点无法正常服务,则集群建立失败。接着,发送INFO命令,确定每个节点的运行id以及是否开启集群。

b.向每个节点发送cluster meet命令,格式cluster meet ip port,告诉当前节点指定ip:port的节点也是集群的一个节点。从而使各ip归入一个集群。

c.分配主从数据库节点,原则是尽量让每个主库运行在不同ip,确保系统容灾能力。

d.为每个主库分配插槽,实质就是分配哪些键归哪些节点负责。

e.为每个将要成为从库的库,发送命令CLUSTER REPLICATE 主库运行ID,将当前节点转换成从库,并复制数据。

上述步骤后,集群创建完成,连接上任意节点,输入命令CLUSTER NODES,可以查看集群的情况,包括每个节点运行id、地址、端口、角色、状态、插槽等。

4、节点增加

向加为节点的redis(下面称为A),发送命令CLUSTERMEET ip port,其中ip和端口是目前集群中的任一节点(下面称为B)。

A接到命令后,会与B进行握手,使B认为A是集群的一员。接着,B会使用Gossip协议,将A的信息通知集群的其他节点。

5、插槽(Slot)分配

新节点可以执行clusterreplicate成为某个主节点的从节点,也可以申请一个插槽作为主节点运行。

1)键与插槽的关系

redis将每个键的键名有效部分用CRC16算法,计算散列值,再取对16384的余数。这样使每个键都可以分配到16384个插槽中。进而分配指定节点处理。因此,集群最大节点数量是16384,通常建议在1000左右。

有效键名指:

a.如果键名包含{,且后面有},且{}之间至少1个字符,则有效键名是{}之间的内容。

b.如果不满足上述条件,则键名是整个数据。

这样做的好处在于,可以把某个系统都设置成一样的,例如{order}:id,{order}:user:orderid,这两个键由于都有{},且里面的内容不止1个字符,因此有效键名都是order,则会被分配到1个节点进行存储,也就可以使用MGET等批量操作进行查询。

2)插槽分配指定节点

a.插槽没有分配过

在要分配的节点,执行命令clusteradd slot1 [slot2 slot3….]进行分配,如果某个插槽已经分配节点,会报错。

可以通过clusterslots查看当前插槽的情况。

b.分配过的插槽重新分配

可以使用redis-trib.rbreshard 待分片ip:端口,然后跟着命令提示输入迁移插槽数量、迁移至的运行id、迁移前的运行id,完成迁移。(推荐使用此方法)

也可以直接执行命令:cluster setslots 插槽号node 新的运行id。但是由于迁移不会把键也迁移过去,因此如果插槽里面有键会丢失,所以要按下列方式进行迁移。

a. 获取插槽存在的所有键:clustergetkeysinslot 插槽号要返回键的数量

b. 对每个键迁移:migrate 目标节点地址 目标节点端口 键名 数据库号码 超时时间 [copy] [replace]。[copy]则表示不删除当前键,是在新地址建立副本;[replace]表示目标地址如果有同名的键,则覆盖目标地址的同名键。

c. 重新分配插槽:clustersetslots 插槽号node 新的运行id。

3)迁移过程的数据问题

迁移过程中,如果时间较久,可能会存在数据不一致问题。redis提供解决方案,实现集群在线情况下的插槽迁移。

命令:

clustersetslot 插槽号migrating 新节点的运行id

cluster setslot 插槽号 importing新节点的运行id

如果要把0号插槽从A移到B,需要如下操作(redis-trib.rb的方案):

a. B执行 clustersetslot 0 importing A

b. A执行 clustersetslot 0 migrating B

c. 执行cluster getkeysinslot0获取0号插槽的键列表

d. 对第三步的每个键执行migrate,迁移键

e. clustersetslots 0 node B 完成迁移

即多了a、b两个步骤,执行完前两步时,客户端A请求插槽0中的键时,如果键存在(未迁移),则正常处理;如果不存在,则返回ASK跳转请求,告诉客户端键在B。客户端接到ASK后,向B发送ASKING,再发送请求数据的命令。

相反,如果客户端向B发送请求,如果有ASKING,则返回数据,否则B返回MOVED跳转,要求客户端去A请求数据。

6、获取键对应的插槽

客户端发送请求键的操作,如果某个节点有该键,则返回;否则返回重定向请求。这个重定向请求,如果开发语言已经封装,则对于开发者是透明的,相当于单机。如果没有封装,则需要手工实现重定向。

重定向的返回:MOVED 插槽号 IP:port,客户端收到重定向后,需要重定向到IP:port的地方,去进行键的相关操作。

redis-cli模式下,在对节点进行初始化连接的时候,加上-c参数,如果后续客户端发送过来该阶段不存在的键,会自动重定向。

优化:

重定向使得原本一次连接可以完成的操作,需要两次连接。反复这样会影响性能,通常需要客户端缓存插槽信息,后面就直接往相应的插槽发送数据。

7、故障恢复

集群内的每个节点,每秒都会随机选5个节点,再选这5个节点中最久没有响应的节点发送ping命令。如果一定时间内没有回复,会被认为疑似下线(PFAIL),与哨兵的主观下线相类似,如果要真正确认节点下线,还需要一定数量的节点认为该阶段疑似下线。

过程如下:

1)节点A认为节点B疑似下线,会和集群的其他节点广播。

2)当集群中的某一节点C收到集群中半数以上的节点认为B疑似下线,则确认B下线,并进行广播。

当集群中的一个主库下线,至少需要1个从库顶上,因此,集群中每个主库至少要有一个从库。

选择哪个从库作为新主库,和哨兵的方式一样,Raft算法,如下:

1)发现主库下线的从库A,向集群其他节点发送请求,要求称为主库。

2)集群的其他节点如果没有选其他的从库,则选A作为主库。

3)A发现集群半数以上节点选A做主库,则A就成为主库。

4)当多个库参选,出现没有库可以当选主库的情况,则这些库都随机等待一个时间再发送请求。

当某个从库变成主库后,会通过命令Slaveof no one将自己变成主库,并把旧主库的插槽转给自己负责。

当某个主库负责1个以上的插槽,且该主库下线,且没有可用的从库时,默认情况认为该集群已经不可用,集群下线。如果希望集群不下线继续工作,可以通过配置文件cluster-require-full-coverage no(默认是yes)。

——written by linhxx 2107.08.12

原文发布于微信公众号 - 决胜机器学习(phpthinker)

原文发表时间:2017-08-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Ceph对象存储方案

Luminous版本PG 分布调优

Luminous版本开始新增的balancer模块在PG分布优化方面效果非常明显,操作也非常简便,强烈推荐各位在集群上线之前进行这一操作,能够极大的提升整个集群...

3655
来自专栏pangguoming

Spring Boot集成JasperReports生成PDF文档

由于工作需要,要实现后端根据模板动态填充数据生成PDF文档,通过技术选型,使用Ireport5.6来设计模板,结合JasperReports5.6工具库来调用渲...

1.4K7
来自专栏张善友的专栏

LINQ via C# 系列文章

LINQ via C# Recently I am giving a series of talk on LINQ. the name “LINQ via C...

3005
来自专栏一个爱瞎折腾的程序猿

sqlserver使用存储过程跟踪SQL

USE [master] GO /****** Object: StoredProcedure [dbo].[sp_perfworkload_trace_s...

2850
来自专栏我和未来有约会

Silverlight第三方控件专题

这里我收集整理了目前网上silverlight第三方控件的专题,若果有所遗漏请告知我一下。 名称 简介 截图 telerik 商 RadC...

4385
来自专栏落花落雨不落叶

canvas画简单电路图

83511
来自专栏张善友的专栏

Miguel de Icaza 细说 Mix 07大会上的Silverlight和DLR

Mono之父Miguel de Icaza 详细报道微软Mix 07大会上的Silverlight和DLR ,上面还谈到了Mono and Silverligh...

2997
来自专栏张善友的专栏

Silverlight + Model-View-ViewModel (MVVM)

     早在2005年,John Gossman写了一篇关于Model-View-ViewModel模式的博文,这种模式被他所在的微软的项目组用来创建Expr...

3278
来自专栏一个会写诗的程序员的博客

Spring Reactor 项目核心库Reactor Core

Non-Blocking Reactive Streams Foundation for the JVM both implementing a Reactiv...

2752
来自专栏Golang语言社区

【Golang语言社区】GO1.9 map并发安全测试

var m sync.Map //全局 func maintest() { // 第一个 YongHuomap := make(map[st...

5408

扫码关注云+社区