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 条评论
登录 后参与评论

相关文章

来自专栏个人分享

Spark性能测试报告与调优参数

1、代码中尽量避免group by函数,如果需要数据聚合,group形式的为rdd.map(x=>(x.chatAt(0),x)).groupbyKey().m...

511
来自专栏owent

关于BUS通信系统的一些思考(二)

虽然我很不愿意再设计一套BUS系统,但是现有的一些确实都没有特别符合我的口味的。所以还是尝试设计一个出来。

763
来自专栏MYSQL轻松学

MySQL Innodb MTR源码解析

最近看了下Mysql innodb源码MTR模块,了解源码能帮助DBA更熟悉数据库运行原理、更容易定位排查问题。那么什么是Mtr?Mtr究竟是用来做什么的?围绕...

3616
来自专栏腾讯开源的专栏

微信开源 libco :简单易用高性能的协程库

libco 是微信后台大规模使用的 c/c++ 协程库,2013年至今稳定运行在微信后台的数万台机器上。libco 在2013年的时候作为腾讯六大开源项目首次开...

8770
来自专栏开发与安全

linux系统编程之基础必备(七):read/write函数与(非)阻塞I/O的概念

一、read/write 函数 read函数从打开的设备或文件中读取数据。 #include <unistd.h> ssize_t read(int fd,...

1920
来自专栏idba

如何做一个靠谱的发号器

在使用数据库时,表的主键经常会使用数据库的自增(auto_increment)来产生。这当然很方便也很高效。但是使用自增也会带来一些麻烦。如果从一个数据库以外的...

936
来自专栏coder修行路

多进程、协程、事件驱动及select poll epoll

多线程的使用场景 IO操作不占用CPU 计算占用cpu python多线程不适合cpu密集型操作的任务,适合IO操作密集型的任务 多进程 简单的一个多进程例子:...

2689
来自专栏Albert陈凯

mapreduce项目调优

一、调优的目的 充分的利用机器的性能,更快的完成mr程序的计算任务。甚至是在有限的机器条件下,能够支持运行足够多的mr程序。 二、调优的总体概述 从mr程...

3166
来自专栏python3

python3--协程,greenlet模块,gevent模块

之前学习了线程、进程的概念,了解了在操作系统中进程是资源分配的最小单位,线程是CPU调度的最小单位。按道理来说我们已经算是把cpu的利用率提高很多了。但是我们知...

782
来自专栏从零开始的linux

安装Elasticsearch5

新特性 支持lucence 6.x:索引性能提升 新增sliced scroll类型:并发遍历 新增profile API:查询优化 新增reindex:对数据...

2914

扫描关注云+社区