Redis系列1:深刻理解高性能Redis的本质

Redis系列2:数据持久化提高可用性

Redis系列3:高可用之主从架构

Redis系列4:高可用之Sentinel(哨兵模式)

1 背景

前面我们学习了Redis高可用的两种架构模式:主从模式哨兵模式

解决了我们在Redis实例发生故障时,具备主从自动切换、故障转移的能力,最终保证服务的高可用。

但是这些其实远远不够,随着我们业务规模的不断扩展,用户量膨胀,并发量持续提升。原有的主从架构,已经远远达不到我们的需求了,这时候会有一些问题出现,比如:

  • 单机的CPU、内存、连接数、计算力都是有极限的,不能无限制的承载流量的扩增。
  • 超额的请求、大规模的数据计算,导致必然的慢响应。

    这时候就需要适当的推进架构的演进,来满足发展的需要。

2 Cluster 模式介绍

2.1 什么是Cluster模式

Cluster 即 集群模式,类似MySQL,Redis 集群也是一种分布式数据库方案,集群通过分片(sharding)模式来对数据进行管理,并具备分片间数据复制、故障转移和流量调度的能力。这种 分治模式很常见,我们在 微服务系列:拆分策略MySQL系列:分库分表 中实践过很多次了。

Redis集群的做法是 将数据划分为 16384(2的14次方)个哈希槽(slots),如果你有多个实例节点,那么每个实例节点将管理其中一部分的槽位,槽位的信息会存储在各自所归属的节点中。以下图为例,该集群有4个 Redis 节点,每个节点负责集群中的一部分数据,数据量可以不均匀。比如性能好的实例节点可以多分担一些压力。

一个Redis集群一共有16384个哈希槽,你可以有1 ~ n个节点来分配这些哈希槽,可以不均匀分配,每个节点可以处理0个 到至多 16384 个槽点。

当16384个哈希槽都有节点进行管理的时候,集群处于online 状态。同样的,如果有一个哈希槽没有被管理到,那么集群处于offline状态。

上面图中4个实例节点组成了一个集群,集群之间的信息通过 Gossip协议 进行交互,这样就可以在某一节点记录其他节点的哈希槽(slots)的分配情况。

2.2 为什么需要Cluster模式

单机的吞吐无法承受持续扩增的流量的时候,最好的办法是从横向(scale out) 和 纵向(scale up)两方面进行扩展,这个我们在 MySQL系列 和 微服务系列 的时候已经讨论过了。

  • 纵向扩展(scale up):将单个实例的硬件资源做提升,比如 CPU核数量、内存容量、SSD容量。
  • 横向扩展(scale out):横向扩增 Redis 实例数,这样每个节点只负责一部分数据就可以,分担一下压力,典型的分治思维。



    那横向扩展和纵向扩展各有什么优缺点呢?
  • scale up 虽然操作起来比较简易。但是没法解决Redis一些瓶颈问题,比如持久化(如轮式RDB快照还是AOF指令),遇到大数据量的时候,照样效率会很低,响应慢。另外,单台服务机硬件扩容也是有限制的,不可能无限操作。
  • scale out 更容易扩展,分片的模式可以解决很多问题,包括单一实例节点的硬件扩容限制、成本限制,还可以分摊压力,精细化治理,精细化维护。但是同时也要面临分布式带来的一些问题

    现实情况下,在面对千万级甚至亿级别的流量的时候,很多大厂都是在千百台的实例节点组成的集群上进行流量调度、服务治理的。所以,使用Cluster模式,是业内广泛采用的模式。

3 Cluster 实现原理

3.1 集群的组群过程

集群是由一个个互相独立的节点(readis node)组成的, 所以刚开始的时候,他们都是隔离,毫无联系的。我们需要通过一些操作,把他们聚集在一起,最终才能组成真正的可协调工作的集群。

各个节点的联通是通过 CLUSTER MEET 命令完成的:CLUSTER MEET <ip> <port>

具体的做法是其中一个node向另外一个 node(指定 ip 和 port) 发送 CLUSTER MEET 命令,这样就可以让两个节点进行握手(handshake操作) ,握手成功之后,node 节点就会将握手另一侧的节点添加到当前节点所在的集群中。

这样一步步的将需要聚集的节点都圈入同一个集群中,如下图:

3.2 集群数据分片原理

现在的Redis集群分片的做法,主要是使用了官方提供的 Redis Cluster 方案。这种方案就是的核心就是集群的实例节点与哈希槽(slots)之间的划分、映射与管理。下面我们来看看他具体的步骤。

3.2.1 哈希槽(slots)的划分

这个前面已经说过了,我们会将整个Redis数据库划分为16384个哈希槽,你的Redis集群可能有n个实例节点,每个节点可以处理0个 到至多 16384 个槽点,这些节点把 16384个槽位瓜分完成。

而你实际存储的Redis键值信息也必然归属于这 16384 个槽的其中一个。slots 与 Redis Key 的映射是通过以下两个步骤完成的:

  • 使用 CRC16 算法计算键值对信息的Key,会得出一个 16 bit 的值。
  • 将 第1步中得到的 16 bit 的值对 16384 取模,得到的值会在 0 ~ 16383 之间,映射到对应到哈希槽中。

    当然,可能在一些特殊的情况下,你想把某些key固定到某个slot上面,也就是同一个实例节点上。这时候可以用hash tag能力,强制 key 所归属的槽位等于 tag 所在的槽位。

    其实现方式为在key中加个{},例如test_key{1}。使用hash tag后客户端在计算key的crc16时,只计算{}中数据。如果没使用hash tag,客户端会对整个key进行crc16计算。下面演示下hash tag使用:
127.0.0.1:6380> cluster keyslot user:case{1}
(integer) 1024
127.0.0.1:6380> cluster keyslot user:favor
(integer) 1023
127.0.0.1:6380> cluster keyslot user:info{1}
(integer) 1024

如上,使用hash tag 后会对应到通一个hash slot:1024中。

3.2.2 哈希槽(slots)的映射

一种是初始化的时候均匀分配 ,使用 cluster create 创建,会将 16384 个slots 平均分配在我们的集群实例上,比如你有n个节点,那每个节点的槽位就是 16384 / n 个了 。

另一种是通过 CLUSTER MEET 命令将 node1、node2、ndoe3、node4 4个节点联通成一个集群,刚联通的时候因为还没分配哈希槽,还是处于offline状态。我们使用 cluster addslots 命令来指定。

指定的好处就是性能好的实例节点可以多分担一些压力。

可以通过 addslots 命令指定哈希槽范围,比如下图中,我们哈希槽是这么分配的:实例 1 管理 0 ~ 7120 哈希槽,实例 2 管理 7121~9945 哈希槽,实例 3 管理 9946 ~ 13005 哈希槽,实例 4 管理 13006 ~ 16383 哈希槽。

redis-cli -h 192.168.0.1 –p 6379 cluster addslots 0,7120
redis-cli -h 192.168.0.2 –p 6379 cluster addslots 7121,9945
redis-cli -h 192.168.0.3 –p 6379 cluster addslots 9946,13005
redis-cli -h 192.168.0.4 –p 6379 cluster addslots 13006,16383

slots 和 Redis 实例之间的映射关系如下:

key testkey_1testkey_2 经过 CRC16 计算后再对slots的总个数 16384 取模,结果分别匹配到了 cache1 和 cache3 上。

3.3 数据复制过程和故障转移

3.3.1 数据复制

Cluster 是具备Master 和 Slave模式,Redis 集群中的每个实例节点都负责一些槽位,比如上图中的四个节点分管了不同的槽位区间。而每个Master至少需要一个Slave节点,Slave 节点是通过《Redis系列3:高可用之主从架构》方式同步主节点数据。 节点之间保持TCP通信,当Master发生了宕机, Redis Cluster自动会将对应的Slave节点选为Master,来继续提供服务。与纯主从模式不同的是,主从节点之间并没有读写分离, Slave 只用作 Master 宕机的高可用备份,所以更合理来说应该是主备模式。

如果主节点没有从节点,那么一旦发生故障时,集群将完全处于不可用状态。 但也允许配置 cluster-require-full-coverage参数,及时部分节点不可用,其他节点正常提供服务,这是为了避免全盘宕机。

主从切换之后,故障恢复的主节点,会转化成新主节点的从节点。这种自愈模式对提高可用性非常有帮助。

3.3.2 故障检测

一个节点认为某个节点宕机不能说明这个节点真的挂起了,无法提供服务了。只有占据多数的实例节点都认为某个节点挂起了,这时候cluster才进行下线和主从切换的工作。

Redis 集群的节点采用 Gossip 协议来广播信息,每个节点都会定期向其他节点发送ping命令,如果接受ping消息的节点在指定时间内没有回复pong,则会认为该节点失联了(PFail),则发送ping的节点就把接受ping的节点标记为主观下线。

如果集群半数以上的主节点都将主节点 xxx 标记为主观下线,则节点 xxx 将被标记为客观下线,然后向整个集群广播,让其它节点也知道该节点已经下线,并立即对下线的节点进行主从切换。

3.3.3 主从故障转移

当一个从节点发现自己正在复制的主节点进入了已下线,则开始对下线主节点进行故障转移,故障转移的步骤如下:

  • 如果只有一个slave节点,则从节点会执行SLAVEOF no one命令,成为新的主节点。

  • 如果是多个slave节点,则采用选举模式进行,竞选出新的Master

    • 集群中设立一个自增计数器,初始值为 0 ,每次执行故障转移选举,计数就会+1。
    • 检测到主节点下线的从节点向集群所有master广播一条CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,所有收到消息、并具备投票权的主节点都向这个从节点投票。
    • 如果收到消息、并具备投票权的主节点未投票给其他从节点(只能投一票哦,所以投过了不行),则返回一条CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示支持。
    • 参与选举的从节点都会接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,如果收集到的选票 大于等于 (n/2) + 1 支持,n代表所有具备选举权的master,那么这个从节点就被选举为新主节点。
    • 如果这一轮从节点都没能争取到足够多的票数,则发起再一轮选举(自增计数器+1),直至选出新的master。
  • 新的主节点会撤销所有对已下线主节点的slots指派,并将这些slots全部指派给自己。

  • 新的主节点向集群广播一条PONG消息,这条PONG消息可以让集群中的其他节点立即知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽。

  • 新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成。

跟哨兵类似,两者都是基于 Raft 算法来实现的,流程如图所示:

3.4 client 访问 数据集群的过程

3.4.1 定位数据所在节点

我们前面说过了,Redis 中的每个实例节点会将自己负责的哈希槽信息 通过 Gossip 协议广播给集群中其他的实例,实现了slots分配信息的扩散。这样的话,每个实例都知道整个集群的哈希槽分配情况以及映射信息。

所以客户端想要快捷的连接到服务端,并对某个redis数据进行快捷访问,一般是经过以下步骤:

  • 客户端连接任一实例,获取到slots与实例节点的映射关系,并将该映射关系的信息缓存在本地。
  • 将需要访问的redis信息的key,经过CRC16计算后,再对16384 取模得到对应的 Slot 索引。
  • 通过slot的位置进一步定位到具体所在的实例,再将请求发送到对应的实例上。

    下图展示了 Redis 客户端如何定位数据所在节点:

4 总结

  • 哨兵模式已经实现了故障自动转移的能力,但业务规模的不断扩展,用户量膨胀,并发量持续提升,会出现了 Redis 响应慢的情况。
  • 使用 Redis Cluster 集群,主要解决了大数据量存储导致的各种慢问题,同时也便于横向拓展。在面对千万级甚至亿级别的流量的时候,很多大厂的做法是在千百台的实例节点组成的集群上进行流量调度、服务治理的。
  • 整个Redis数据库划分为16384个哈希槽,Redis集群可能有n个实例节点,每个节点可以处理0个 到至多 16384 个槽点,这些节点把 16384个槽位瓜分完成。
  • Cluster 是具备Master 和 Slave模式,Redis 集群中的每个实例节点都负责一些槽位,节点之间保持TCP通信,当Master发生了宕机, Redis Cluster自动会将对应的Slave节点选为Master,来继续提供服务。
  • 客户端能够快捷的连接到服务端,主要是将slots与实例节点的映射关系存储在本地,当需要访问的时候,对key进行CRC16计算后,再对16384 取模得到对应的 Slot 索引,再定位到相应的实例上。实现高效的连接。

Redis系列5:深入分析Cluster 集群模式的更多相关文章

  1. Azure Redis Cache (5) Redis Cache Cluster集群模式

    <Windows Azure Platform 系列文章目录> Redis Cluster 3.0之后的版本,已经支持Redis Cluster集群模式,Redis Cluster采用无中 ...

  2. 2、Redis 底层原理:Cluster 集群部署与详解

    Redis 简介 Redis 提供数据缓存服务,内部数据都存在内存中,所以访问速度非常快. 早期,Redis 单应用服务亦能满足企业的需求.之后,业务量的上升,单机的读写能力满足不了业务的需求,技术上 ...

  3. Redis学习笔记八:集群模式

    作者:Grey 原文地址:Redis学习笔记八:集群模式 前面提到的Redis学习笔记七:主从复制和哨兵只能解决Redis的单点压力大和单点故障问题,接下来要讲的Redis Cluster模式,主要是 ...

  4. redis cluster(集群)模式的创建方式

    redis常用的架构有三种,单例.哨兵.集群,其他的都说过了,这里只简单介绍集群搭建. 单例最简单没什么好说的. 哨兵之前说过,该模式下有哨兵节点监视master和slave,若master宕机可自动 ...

  5. redis 学习笔记(6)-cluster集群搭建

    上次写redis的学习笔记还是2014年,一转眼已经快2年过去了,在段时间里,redis最大的变化之一就是cluster功能的正式发布,以前要搞redis集群,得借助一致性hash来自己搞shardi ...

  6. Redis学习笔记~conf自主集群模式

    回到目录 Redis自主提供了集群模式,当然也只是比较简单的读写分离模式,或者叫主从模式,它在各个redis服务端自己做数据同步机制,当然就是将主服务端的信息同步到各个slave服务器上,在客户端集成 ...

  7. 关于redis主从|哨兵|集群模式

    关于redis主从.哨兵.集群的介绍网上很多,这里就不赘述了. 一.主从 通过持久化功能,Redis保证了即使在服务器重启的情况下也不会损失(或少量损失)数据,因为持久化会把内存中数据保存到硬盘上,重 ...

  8. redis主从|哨兵|集群模式

    关于redis主从.哨兵.集群的介绍网上很多,这里就不赘述了. 一.主从 通过持久化功能,Redis保证了即使在服务器重启的情况下也不会损失(或少量损失)数据,因为持久化会把内存中数据保存到硬盘上,重 ...

  9. Redis cluster集群模式的原理

    redis cluster redis cluster是Redis的分布式解决方案,在3.0版本推出后有效地解决了redis分布式方面的需求 自动将数据进行分片,每个master上放一部分数据 提供内 ...

随机推荐

  1. layui数据表格导入数据

    作为一个后端程序员,前端做的确实很丑,所以就学习了一下layui框架的使用.数据表格主要的问题就是传输数据的问题,这里我用我的前后端代码来做一个实际的分解. 前端部分 可以到layui官网示例中找到数 ...

  2. 找到占用CPU最高的Java线程

    一.找到java进程id jps查看当前运行的java进程id [root@localhost ~]# jps 18354 Jps 9381 Bootstrap 二.找到内存和CPU占用最高的线程pi ...

  3. awk内建函数

    内建函数 length() 获得字符串长度 cat score.txt Marry 2143 78 84 77 Jack 2321 66 78 45 Tom 2122 48 77 71 Mike 25 ...

  4. 基于.NetCore开发博客项目 StarBlog - (7) 页面开发之文章详情页面

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  5. Nginx分片限流

    一.全局限流 在http节点中添加 # 创建限流规则 limit_req_zone $binary_remote_addr zone=addr:10m rate=1r/s;# 客户端进行限流 # li ...

  6. linux篇--mysql数据库备份并删除前一分钟的数据

    linux 中mysql数据库定时备份并删除前一分钟的所有数据 #!/bin/bash #mysqldump -uroot -ppassword01! imaginebase > /home/b ...

  7. 将Abp移植进.NET MAUI项目(一):搭建项目

    ​ 前言 去年12月份做了MAUI混合开发框架的调研,想起来文章里给自己挖了个坑,要教大家如何把Abp移植进Maui项目,由于篇幅限制,将分为三个章节. 将Abp移植进.NET MAUI项目(一):搭 ...

  8. 【JNPF修改通告】fastjson≤1.2.80反序列化漏洞

    近日Fastjson Develop Team 发现 fastjson 1.2.80及以下存在新的风险,存在反序列化漏洞.攻击者可绕过默认autoType关闭限制,攻击远程服务器,风险影响较大,请大家 ...

  9. python发QQ邮件

    python发qq邮件相对比较简单,网上教程一大把:固定套路,后面封装看自己怎么方便可以怎样进行封装:原版代码如下: """ # -*- coding : utf-8 - ...

  10. torch.tensor(),torch.Tensor()

    Pytorch tensor操作 https://www.cnblogs.com/jeshy/p/11366269.html    我们需要明确一下,torch.Tensor()是python类,更明 ...