Redis Cluster本身提供了自动将数据分散到Redis Cluster不同节点的能力,分区实现的关键点问题包括:如何将数据自动地打散到不同的节点,使得不同节点的存储数据相对均匀;如何保证客户端能够访问到正确的节点和数据;如何保证重新分片的过程中不影响正常服务。这篇文章通过了解这些问题来认识Redis Cluster分区实现原理。

认识Redis Cluster

Redis Cluster是由多个同时服务于一个数据集合的Redis实例组成的整体,对于用户来说,用户只关注这个数据集合,而整个数据集合的某个数据子集存储在哪个节点对于用户来说是透明的。Redis Cluster具有分布式系统的特点,也具有分布式系统如何实现高可用性与数据一致性的难点,由多个Redis实例组成的Redis Cluster结构通常如下:

Redis Cluster特点如下:

  • 所有的节点相互连接;

  • 集群消息通信通过集群总线通信,,集群总线端口大小为客户端服务端口+10000,这个10000是固定值;

  • 节点与节点之间通过二进制协议进行通信;

  • 客户端和集群节点之间通信和通常一样,通过文本协议进行;

  • 集群节点不会代理查询;

Redis Cluster分区实现原理

槽(slot)概念

Redis Cluster中有一个16384长度的槽的概念,他们的编号为0、1、2、3……16382、16383。这个槽是一个虚拟的槽,并不是真正存在的。正常工作的时候,Redis Cluster中的每个Master节点都会负责一部分的槽,当有某个key被映射到某个Master负责的槽,那么这个Master负责为这个key提供服务,至于哪个Master节点负责哪个槽,这是可以由用户指定的,也可以在初始化的时候自动生成(redis-trib.rb脚本)。这里值得一提的是,在Redis Cluster中,只有Master才拥有槽的所有权,如果是某个Master的slave,这个slave只负责槽的使用,但是没有所有权。Redis Cluster怎么知道哪些槽是由哪些节点负责的呢?某个Master又怎么知道某个槽自己是不是拥有呢?

位序列结构

Master节点维护着一个16384/8字节的位序列,Master节点用bit来标识对于某个槽自己是否拥有。比如对于编号为1的槽,Master只要判断序列的第二位(索引从0开始)是不是为1即可。

如上面的序列,表示当前Master拥有编号为1,134的槽。集群同时还维护着槽到集群节点的映射,是由长度为16384类型为节点的数组实现的,槽编号为数组的下标,数组内容为集群节点,这样就可以很快地通过槽编号找到负责这个槽的节点。位序列这个结构很精巧,即不浪费存储空间,操作起来又很便捷。

键空间分布基本算法

这里讲的是Redis Cluster如何将键空间分布在不同的节点的,键空间意为Redis Cluster所拥有用户所有数据集合的键的取值范围,这个范围叫做键空间。提到空间分布,必然会想到哈希算法,没错,通过哈希算法再加上取模运算可以将一个值固定地映射到某个区间,在这里,这个区间叫做slots,区间由连续的slot组成。在Redis Cluster中,我们拥有16384个slot,这个数是固定的,我们存储在Redis Cluster中的所有的键都会被映射到这些slot中,下面讲讲Redis Cluster是如何做映射的。

键到slot的基本映射算法如下:

HASH_SLOT = CRC16(key) mod 16384

用Redis中的代码表示如下(这个代码被稍微修改了一下,后面会还原):

crc16(key) & 0x3FFF

经过简单的计算就得到了当前key应该是存储在哪个slot里面,值得注意的是,指定的key会被存储在哪个slot,这个关系是铁打不变的。如果我提交了一批命令,往Redis中存储一批键,那么这些键一般会被映射到不同的slot,而不同的slot又可能由Redis Cluster中不同的节点服务,这样就和的预期有点不同,有没有办法将这批键映射到同一个slot呢?答案是可以。

键哈希标签原理

键哈希标签是一种可以让用户指定将一批键都能够被存放在同一个槽中的实现方法,用户唯一要做的就是按照既定规则生成key即可,这个规则是这样的,如果我有对于同一个用户有两种不同含义的两份数据,我只要将他们的键设置为下面即可:

abc{userId}def和ghi{userId}jkl

redis在计算槽编号的时候只会获取{}之间的字符串进行槽编号计算,这样由于上面两个不同的键,{}里面的字符串是相同的,因此他们可以被计算出相同的槽,相关代码如下:

unsigned int keyHashSlot(char *key, int keylen) {

int s, e;

for (s = 0; s < keylen; s++)

if (key[s] == '{') break;

if (s == keylen) return crc16(key,keylen) & 0x3FFF;

for (e = s+1; e < keylen; e++)

if (key[e] == '}') break;

if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF;

return crc16(key+s+1,e-s-1) & 0x3FFF;

}

客户端是怎么在Redis Cluster中找到正确的节点的呢?下面看看。

重定向客户端

文章开始讲到,Redis Cluster并不会代理查询,那么如果客户端访问了一个key并不存在的节点,这个节点是怎么处理的呢?比如我想获取key为msg的值,msg计算出来的槽编号为254,当前节点正好不负责编号为254的槽,那么就会返回客户端下面信息:

GET msg

-MOVED 254 127.0.0.1:6381

表示客户端想要的254槽由运行在IP为127.0.0.1,端口为6381的Master实例服务。如果根据key计算得出的槽恰好由当前节点负责,则当期节点会立即返回结果。这里明确一下,没有代理的Redis Cluster可能会导致客户端两次连接急群中的节点才能找到正确的服务,推荐客户端缓存连接,这样最坏的情况是两次往返通信。

重新分片(Resharding)

重新分片意为槽到集群节点的映射关系要改变,不变的是键到槽的映射关系,因此当重新分片的时候,如果槽中有键,那么键也是要被移动到新的节点的。下面看看重新分片是怎么做的,假如我们有一批槽需要从一个Master节点移动到另一个Master节点:

这里简化模型,假设这批待迁移的槽编号为1、2、3,并假设左边的节点为MasterA节点,右边的节点为MasterB节点。

槽迁移的过程

槽迁移的过程中有一个不稳定状态,这个不稳定状态会有一些规则,这些规则定义客户端的行为,从而使得Redis Cluster不必宕机的情况下可以执行槽的迁移。下面这张图描述了我们迁移编号为1、2、3的槽的过程中,他们在MasterA节点和Master节点中的状态。

MIGRATING状态

本例中MIGRATING状态是发生在MasterA节点中的一种槽的状态,预备迁移槽的时候槽的状态首先会变为MIGRATING状态,这种状态的槽会实际产生什么影响呢?当客户端请求的某个Key所属的槽处于MIGRATING状态的时候,影响有下面几条:

  1. 如果Key存在则成功处理

  2. 如果Key不存在,则返回客户端ASK,仅当这次请求会转向另一个节点,并不会刷新客户端中node的映射关系,也就是说下次该客户端请求该Key的时候,还会选择MasterA节点

  3. 如果Key包含多个命令,如果都存在则成功处理,如果都不存在,则返回客户端ASK,如果一部分存在,则返回客户端TRYAGAIN,通知客户端稍后重试,这样当所有的Key都迁移完毕的时候客户端重试请求的时候回得到ASK,然后经过一次重定向就可以获取这批键

IMPORTING状态

本例中的IMPORTING状态是发生在MasterB节点中的一种槽的状态,预备将槽从MasterA节点迁移到MasterB节点的时候,槽的状态会首先变为IMPORTING。IMPORTING状态的槽对客户端的行为有下面一些影响:

  1. 正常命令会被MOVED重定向,如果是ASKING命令则命令会被执行,从而Key没有在老的节点已经被迁移到新的节点的情况可以被顺利处理;

  2. 如果Key不存在则新建;

  3. 没有ASKING的请求和正常请求一样被MOVED,这保证客户端node映射关系出错的情况下不会发生写错;

键空间迁移

键空间迁移是指当满足了槽迁移前提的情况下,我们就可以通过相关命令将槽1、2、3中的键空间从MasterA节点转移到MasterB节点,这个过程真正实现数据转移。相关命令:

MIGRATE

  1. DUMP

  2. RESTORE

  3. DEL

MIGRATE命令通过三步将数据转移,示意图如下:

经过上面三步可以将键空间数据迁移,然后再将处于MIGRATING和IMPORTING状态的槽变为常态即可,完成整个重新分片的过程。然而MIGRATE并不是原子的,如果在MIGRATE出现错误的情况可能会导致下面问题:

  • 键空间在两个节点都存在;

  • 键空间只存在第一个节点;

总结

本文介绍Redis Cluster分区实现原理主要关注三个问题,1)数据是如何被自动分散到不同的节点的;2)客户端是如何能够正确找到节点的;3)键空间迁移过程是怎么样的?其次是一些实现小细节,通过了解这些问题更好地了这些问题从而更好地认识Redis Cluster分区功能。

Redis Cluster 分区实现原理的更多相关文章

  1. Redis cluster的核心原理分析

    一.节点间的内部通信机制 1.基础通信原理 (1)redis cluster节点间采取gossip协议进行通信 跟集中式不同,不是将集群元数据(节点信息,故障,等等)集中存储在某个节点上,而是互相之间 ...

  2. 近千节点的Redis Cluster高可用集群案例:优酷蓝鲸优化实战(摘自高可用架构)

    (原创)2016-07-26 吴建超 高可用架构导读:Redis Cluster 作者建议的最大集群规模 1,000 节点,目前优酷在蓝鲸项目中管理了超过 700 台节点,积累了 Redis Clus ...

  3. redis cluster 介绍

    介绍 1. cluster的作用 (1)自动将数据进行分片,每个master上放一部分数据 (2)提供内置的高可用支持,部分master不可用时,还是可以继续工作的 2. redis集群实现方案 关于 ...

  4. 关于redis的几件小事(十)redis cluster模式

    redis cluster是redis提供的集群模式. 1.redis cluster的架构 ①可以有多个master node,每个master node 都可以挂载多个slave node. ②读 ...

  5. 全面剖析Redis Cluster原理和应用 (转)

    1.Redis Cluster总览 1.1 设计原则和初衷 在官方文档Cluster Spec中,作者详细介绍了Redis集群为什么要设计成现在的样子.最核心的目标有三个: 性能:这是Redis赖以生 ...

  6. 全面剖析Redis Cluster原理和应用

    全面剖析Redis Cluster原理和应用 1.Redis Cluster总览 1.1 设计原则和初衷 在官方文档Cluster Spec中,作者详细介绍了Redis集群为什么要设计成现在的样子.最 ...

  7. Redis cluster集群:原理及搭建

    Redis cluster集群:原理及搭建 2018年03月19日 16:00:55 阅读数:6120 1.为什么使用redis? redis是一种典型的no-sql 即非关系数据库 像python的 ...

  8. Redis Cluster实现原理

    一.Redis Cluster主要特性和设计     集群目标 1)高性能和线性扩展,最大可以支撑到1000个节点:Cluster架构中无Proxy层,Master与slave之间使用异步replic ...

  9. 全面剖析Redis Cluster原理和应用 (good)

    redis redis cluster注意的问题 : 1.‘cluster-require-full-coverage’参数的设置.该参数是redis配置文件中cluster模式的一个参数,从字面上基 ...

随机推荐

  1. SharePoint 部署时报错: 未能提取此解决方案中的cab文件

    在vs里右击SharePoint项目,选择"部署",结果报错: Error occurred in deployment step 'Add Solution':Fail to e ...

  2. Android开发学习—— ContentProvider内容提供者

    * 应用的数据库是不允许其他应用访问的* 内容提供者的作用就是让别的应用访问到你的数据库.把私有数据暴露给其他应用,通常,是把私有数据库的数据暴露给其他应用. Uri:包含一个具有一定格式的字符串的对 ...

  3. redis数据结构存储Linked List设计细节(redis的设计与实现笔记)

    redis里拥有一个灵活扩展且获取表头表尾复杂度为O(1)的双端列表,分为list和listNode2部分组成. list: typedef struct list {//链表 listNode *h ...

  4. Java 中一个过时的类,能够很好的统计单个字符串的次数

    //StringTokenizer st1=new StringTokenizer("rirtirtjdlkfjlksadfoiyetryetretrejtlkjdsflkiuetuiojr ...

  5. Eclipse使用Maven创建web3.0项目

    安装Maven插件 这一步不细说了,自己下载的Eclipse-JAVA EE 版已自带 Maven插件 开始创建 文本1New一个 Maven Web App项目:File-->New--> ...

  6. 机器学习实战笔记(Python实现)-03-朴素贝叶斯

    --------------------------------------------------------------------------------------- 本系列文章为<机器 ...

  7. Linux下用ftp更新web内容!

    使用ftp更新web!让网页更新一次OK! 配置如下: 1.在Linux下安装ftp服务器! yum -y install vsftpd #ftp由vsftpd提供! 2.配置主配置文件/etc/vs ...

  8. mongodb管理篇

    一.  管理工具集 数据迁移 Mongoexport:用于针对colletions的数据导出,或者打开单个字段. Mongodbimport:与只对应,这个表示在导出的基础上导入. 数据库的备份恢复 ...

  9. AC日记——砍树 codevs 1388

    1388 砍树  时间限制: 1 s  空间限制: 256000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Description 伐木工人米尔科需要砍倒M米长的木 ...

  10. iOS开发中的这些权限,你搞懂了吗?

    APP开发避免不开系统权限的问题,如何在APP以更加友好的方式向用户展示系统权限,似乎也是开发过程中值得深思的一件事. 那如何提高APP获取iOS系统权限的通过率呢?有以下几种方式:1.在用户打开AP ...