Redis进阶实践之十一 Redis的Cluster集群搭建


1、引言

        本文档只对Redis的Cluster集群作简单的介绍,并无对分布式系统的所涉及到的概念作深刻的探讨。本文只是针对如何设置集群、测试和操做集群作了简述,而且从用户的角度描述了系统的行为,并不涉及Redis集群规范中所包含的细节。可是,本教程试图从最终用户的角度来解释有关Redis的Cluster集群的可用性和一致性的特色,并以简单易懂的方式讲解。

        请注意,本教程须要使用Redis 3.0版本或更高版本。

        若是您打算部署Redis的Cluster集群,即便不是严格的要求,咱们也建议阅读更正式的规范。不过,从这篇文档开始,咱们能够先使用Redis Cluster集群,而后再阅读规范也是一个不错的主意。

2、Redis的Cluster模式介绍html


  一、Redis群集101

          Redis集群提供了一种运行Redis设备的方式,而且数据能够在多个Redis节点间自动分配的。Redis集群在分区期间也能提供必定程度的可用性,实际上,就是说当某些节点发生故障或没法通讯时,集群可以继续运行。 可是,若是发生较大故障(例如,大多数主站服务器不可用时),群集会中止运行。

         那么从实际角度而言,您使用Redis Cluster能得到什么呢?

         一、在多个节点之间自动分割数据集的能力。

         二、在节点子集遇到故障或没法与集群其他部分通讯时继续运行的能力。


  二、Redis群集TCP端口

          每一个Redis群集的节点都须要打开两个TCP链接,因为这两个链接就须要两个端口,分别是用于为客户端提供服务的常规Redis TCP命令端口(例如6379)以及经过将10000和命令端口相加(10000+6379)而得到的端口,就是集群端口(例如16379)。

          第二个大号端口用于群集总线,即便用二进制协议的节点到节点通讯通道。 节点使用群集总线进行故障检测,配置更新,故障转移受权等。 客户端不该尝试与群集总线端口通讯,为了保证Redis命令端口的正常使用,请确保在防火墙中打开这两个端口,不然Redis群集节点将没法通讯。

         命令端口集群总线端口偏移量是固定的,始终为10000。

         请注意,为了让Redis群集正常工做,您须要为每一个节点:

           一、用于与客户端进行通讯的普通客户端通讯端口(一般为6379)对全部须要到达群集的客户端以及全部其余群集节点(使用客户端端口进行密钥迁移)都是开放的。

           二、集群总线端口(客户端端口+ 10000)必须可从全部其余集群节点访问。

         若是您不打开这两个TCP端口,则您的群集将没法正常工做。

         集群总线使用不一样的二进制协议进行节点到节点的数据交换,这更适合于使用不多的带宽和处理时间在节点之间交换信息。

     
  三、Redis集群和Docker

        目前,Redis群集不支持NAT地址环境,而且在IP地址或TCP端口被从新映射的通常环境中。

        Docker使用一种叫作端口映射的技术:Docker容器中运行的程序可能会暴露在与程序认为使用的端口不一样的端口上。 这对于在同一服务器中同时使用相同端口运行多个容器颇有用。

        为了使Docker与Redis Cluster兼容,您须要使用Docker的主机联网模式。 请查看Docker文档中的--net = host选项以获取更多信息。


  四、Redis集群数据分片

        Redis集群没有使用一致的散列,而是一种不一样的分片形式,其中每一个 key 在概念上都是咱们称之为散列槽的部分。

        Redis集群中有16384个散列槽,为了计算给定 key 的散列槽,咱们简单地取16384模的CRC16。

        Redis集群中的每一个节点负责哈希槽的一个子集,例如,您可能有一个具备3个节点的集群,其中:

           一、节点A包含从0到5500的散列槽。

           二、节点B包含从5501到11000的散列槽。

           三、节点C包含从11001到16383的散列槽。

        这容许轻松地添加和删除集群中的节点。例如,若是我想添加一个新节点D,我须要将节点A,B,C中的一些散列槽移动到D。一样,若是我想从集群中删除节点A,我能够只移动由A使用的散列槽到B和C,当节点A将为空时,我能够将它从群集中完全删除。

         由于将散列槽从一个节点移动到另外一个节点不须要停机操做,添加和移除节点或更改节点占用的散列槽的百分比也不须要任何停机时间。

         只要涉及单个命令执行(或整个事务或Lua脚本执行)的全部 key 都属于同一散列插槽,Redis群集就支持多个 key 操做。用户可使用称为散列标签的概念强制多个 key 成为同一个散列槽的一部分。

         Hash标记记录在Redis集群规范文档中,但要点是若是在关键字{}括号内有一个子字符串,那么只有该花括号“{}”内部的内容被散列,例如 this{foo}key 和 another{foo}key 保证在同一散列槽中,而且能够在具备多个 key 做为参数的命令中一块儿使用。


  五、Redis集群之主从模型

        为了在主服务器节点的子集失败或不能与大多数节点通讯时保持可用,Redis集群使用主从模型,其中每一个散列槽从1(主服务器自己)到N个副本(N -1个附加从节点)。

         在咱们具备节点A,B,C的示例的群集中,若是节点B失败,则群集没法继续,由于咱们没有办法再在5501-11000范围内提供散列槽。然而,当建立集群时(或稍后),咱们为每一个主服务器节点添加一个从服务器节点,以便最终集群由做为主服务器节点的A,B,C以及做为从服务器节点的A1,B1,C1组成,若是节点B发生故障,系统可以继续运行。节点B1复制B,而且B失败,则集群将促使节点B1做为新的主服务器节点而且将继续正确地操做。

        但请注意,若是节点B和B1在同一时间发生故障,则Redis群集没法继续运行。


  六、Redis集群一致性保证

        Redis 集群没法保证很强的一致性。实际上,这意味着在某些状况下,Redis 集群可能会丢失系统向客户确认的写入。

        Redis集群可能会丢失写入的第一个缘由是由于它使用异步复制。这意味着在写入期间会发生如下事情:

            一、你的客户端写给主服务器节点 B

            二、主服务器节点B向您的客户端回复确认。

            三、主服务器节点B将写入传播到它的从服务器B1,B2和B3。

         正如你能够看到主服务器节点 B 在回复客户端以前不等待B1,B2,B3的确认,由于这会对Redis形成严重的延迟损失,因此若是你的客户端写入了某些东西,主服务器节点 B 确认写入,就在将写入发送给它的从服务器节点存储以前系统崩溃了,其中一个从站(没有收到写入)能够提高为主站,永远丢失写入。

         这与大多数配置为每秒将数据刷新到磁盘的数据库所发生的状况很是类似,由于过去的经验与传统数据库系统有关,不会涉及分布式系统,所以您已经可以推断这种状况。一样,经过强制数据库在回复客户端以前刷新磁盘上的数据,这样能够提升一致性,但这一般会致使性能极低。这与Redis Cluster中的同步复制至关。

         基本上,性能和一致性之间须要权衡。

          Redis集群在绝对须要时也支持同步写入,经过WAIT命令实现,这使得丢失写入的可能性大大下降,但请注意,即便使用同步复制,Redis集群也不可能实现彻底的一致性:老是有可能会发生故常,在没法接受写入的从设备被选为主设备的时候 。

         还有另外一个值得注意的状况,Redis集群也将丢失数据的写入,这种状况发生在网络分区的时候,客户端与包含至少一个主服务器的少数实例隔离。

         以A,B,C,A1,B1,C1三个主站和三个从站组成的6个节点集群为例。还有一个客户,咱们会调用Z1。

         分区发生后,可能在分区的一侧有A,C,A1,B1,C1,另外一侧有B和Z1。

         Z1仍然可以写入B,它也会接受Z1的写入。若是分区在很短的时间内恢复,则群集将正常继续。可是,若是分区使用比较长的时间将B1提高为多数侧分区的主设备,则Z1发送给B的写入操做将丢失。

         请注意,Z1可以发送给B的写入量有一个最大窗口(maximum window):若是分区多数侧有足够的时间选择一个从设备做为主设备,那么少数侧的每一个主节点将中止接受写操做。

         这个时间值是Redis集群很是重要的配置指令,称为 node timeout (节点超时)。

         在节点超时事后,主节点被认为是失效的,而且能够被其副本之一替换。相似地,节点超时事后,主节点没法感知大多数其余主节点,它进入错误状态并中止接受写入。


  七、Redis群集配置参数


         咱们即将建立示例集群部署。在继续以前,让咱们介绍一下Redis Cluster在redis.conf文件中引入的配置参数。有些命令的意思是显而易见的,有些命令在你阅读下面的解释后才会更加清晰。

             一、cluster-enabled <yes/no>:若是想在特定的Redis实例中启用Redis群集支持就设置为yes。 不然,实例一般做为独立实例启动。

             二、cluster-config-file <filename>:请注意,尽管有此选项的名称,但这不是用户可编辑的配置文件,而是Redis群集节点每次发生更改时自动保留群集配置(基本上为状态)的文件,以便可以 在启动时从新读取它。 该文件列出了群集中其余节点,它们的状态,持久变量等等。 因为某些消息的接收,一般会将此文件重写并刷新到磁盘上。

             三、cluster-node-timeout <milliseconds>:Redis群集节点能够不可用的最长时间,而不会将其视为失败。 若是主节点超过指定的时间不可达,它将由其从属设备进行故障切换。 此参数控制Redis群集中的其余重要事项。 值得注意的是,每一个没法在指定时间内到达大多数主节点的节点将中止接受查询。

             四、cluster-slave-validity-factor <factor>:若是设置为0,不管主设备和从设备之间的链路保持断开链接的时间长短,从设备都将尝试故障切换主设备。 若是该值为正值,则计算最大断开时间做为节点超时值乘以此选项提供的系数,若是该节点是从节点,则在主链路断开链接的时间超过指定的超时值时,它不会尝试启动故障切换。 例如,若是节点超时设置为5秒,而且有效因子设置为10,则与主设备断开链接超过50秒的从设备将不会尝试对其主设备进行故障切换。 请注意,若是没有从服务器节点可以对其进行故障转移,则任何非零值均可能致使Redis群集在主服务器出现故障后不可用。 在这种状况下,只有原始主节点从新加入集群时,集群才会返回可用。

             五、cluster-migration-barrier <count>:主设备将保持链接的最小从设备数量,以便另外一个从设备迁移到不受任何从设备覆盖的主设备。有关更多信息,请参阅本教程中有关副本迁移的相应部分。

             六、cluster-require-full-coverage <yes / no>:若是将其设置为yes,则默认状况下,若是key的空间的某个百分比未被任何节点覆盖,则集群中止接受写入。 若是该选项设置为no,则即便只处理关于keys子集的请求,群集仍将提供查询。


3、建立和使用Redis群集

         注意:手动部署Redis群集,这对了解集群的操做细节方面是很是重要的。可是,若是想要启动群集并尽快运行(尽快),请跳过本节和下一节,直接使用create-cluster脚本直接建立Redis群集。

         要建立一个集群,咱们须要作的第一件事是在集群模式下运行几个空的Redis实例。这就意味着群集不是使用普通的Redis实例建立的,由于须要配置特殊模式,以便Redis实例启用群集特定的功能和命令。

         如下是最小的Redis集群配置文件:node

    port 7000
    cluster-enabled yes
    cluster-config-file nodes.conf
    cluster-node-timeout 5000
    appendonly yes


         正如您所看到的那样,启用群集模就是使用 cluster-enabled 这个指令。 每一个Redis的实例还包含存储此节点配置信息的文件的路径,默认状况下为nodes.conf。 这个文件内容永远不要人为地去修改,可是能够修改其名称,它仅在Redis集群实例启动时生成,并在每次须要时进行更新。

         请注意,按预期工做的最小群集须要至少包含三个主节点。 对于第一次测试,强烈建议启动一个由三个主服务器节点和三个从服务器节点组成的六个节点群集。咱们经过如下步骤来一步一步的搭建Redis的Cluster集群环境。

         一、咱们建立相关目录,主文件夹是redis-cluster,在此文件夹下创建6个子文件夹,名称分别是:7000,7001,7002,7003,7004,7005,该目录以咱们将在任何给定目录内运行的实例的端口号命名。

                            

                           而后建立6个子目录,以下图:

                            

redis

      mkdir redis-cluster
      cd redis-cluster
      mkdir 7000 7001 7002 7003 7004 7005

 

                二、目录建立好后,咱们把Redis源文件里面包含的配置文件redis.conf拷贝一份,存放在7000目录下,而后对其配置项进行修改,这个配置文件Redis.conf会做为其余Redis实例的配置文件的模板,并拷贝到其余目录。

                           

        因为咱们是作测试,并无启动6个真正的物理节点,而是把6个Redis实例都部署在了同一台Linux服务器上,地址:192.168.127.130,为了区分Redis实例,咱们是以不一样的端口号来区分Redis实例的。而后咱们修改Redis.conf的配置文件,修改项以下:数据库

      bind 192.168.127.130  //绑定服务器IP地址

      port 7000  //绑定端口号,必须修改,以此来区分Redis实例

      daemonize yes  //后台运行

      pidfile /var/run/redis-7000.pid  //修改pid进程文件名,以端口号命名

      logfile /root/application/program/redis-cluster/7000/redis.log  //修改日志文件名称,以端口号为目录来区分

      dir /root/application/program/redis-cluster/7000/  //修改数据文件存放地址,以端口号为目录名来区分

      cluster-enabled yes  //启用集群

      cluster-config-file nodes-7000.conf  //配置每一个节点的配置文件,一样以端口号为名称

      cluster-node-timeout 15000  //配置集群节点的超时时间,可改可不改

      appendonly yes  //启动AOF增量持久化策略

       appendfsync always  //发生改变就记录日志


        三、7000目录下的Redis.conf配置文件修改后,分别拷贝到其余子目录,依次为:7001,7002,7003,7004,7005,根据上面的配置,咱们只需修改和端口号有关的项目,在Linux系统下,咱们经过命令:%s/7000/7001/g,:%s/7000/7002/g,:%s/7000/7002/g,:%s/7000/7003/g,:%s/7000/7004/g,:%s/7000/7005/g 分别进行全局替换,并保存,完成对其余子目录下的配置文件的修改。

          

        四、咱们安装Redis的Cluster集群,须要使用Ruby命令,因此咱们必须安装对Ruby的支持。

         

                             在此说明一下,之前的Redis版本下,须要安装Ruby和Rubygems,可是最新的版本不须要了,只要安装Ruby,Rubygems就会自动安装。

                                安全

        yum install ruby //安装ruby
        yum install rubygems  //安装rubygems,最新版本会自动安装



        五、咱们安装完 Ruby 和 Rubygems 后,还须要继续安装Redis的Ruby接口程序。ruby

        gem install redis

        安装Redis的ruby接口程序,可能会提示以下,错误:redis requires ruby version 2.2.2,怎么办呢?若是是第一次遇到这个问题,可能会困扰你一阵子,我这里也有解决方案,帮你解忧。地址以下:http://www.cnblogs.com/PatrickLiu/p/8454579.html按步骤执行就能够,一切顺利。
        
        六、开始启动咱们6个Redis实例,而且要指定配置文件,这些配置文件分别在各自的子目录下面。

         

bash

          cd 7000
          redis-server redis.conf

          cd 7001
          redis-server redis.conf

          cd 7002
          redis-server redis.conf
    
          cd 7003
          redis-server redis.conf

          cd 7004
          redis-server redis.conf

          cd 7005
          redis-server redis.conf


        
        七、建立集群,执行redis-trib.rb脚本,这个脚本文件能够拷贝出来,我是把它放在这个目录:/root/application/program/redis/,固然在这个目录下,也有其余文件,好比redis-cli,redis-server等。服务器

        ruby redis-trib.rb  create --replicas 1 192.168.127.130:7000 192.168.127.130:7001 192.168.127.130:7002 192.168.127.130:7003 192.168.127.130:7004 192.168.127.130:7005 

        

           咱们有Redis集群命令行实用程序redis-trib的帮助,Ruby实用程序对实例执行特殊命令以建立新集群,检查或从新设置现有集群,等等。 redis-trib实用程序位于Redis源代码分发的src目录中,固然也能够拷贝到其余目录中,以方便使用。 您须要安装redis gem才能运行redis-trib。网络

        这里使用的命令是create,由于咱们想建立一个新的集群。 选项--replicas 1 意味着咱们须要为每一个建立的主服务器节点建立一个从服务器节点。其余参数是我想用来建立新集群的实例的地址列表。

            显然,咱们要求的惟一设置是建立一个具备3个主站和3个从站的集群。

app

        八、 若是一切顺利,你会看到相似这样的消息: [OK] All 16384 slots covered, 这意味着至少有一个主实例服务于每一个16384可用的插槽,成功建立了Redis的Cluster集群环境。

        

        九、分别登录7000,7001,7002Redis的实例客户端,进行测试。效果如图:

          一、登录7000操做:

          redis-cli -c -h 192.168.127.130 -p 7000


             

                            二、登录7001操做:

          redis-cli -c -h 192.168.127.130 -p 7001


          

                            三、登录7002操做:

          redis-cli -c -h 192.168.127.130 -p 7002


          

        十、经过Cluster Nodes命令和Cluster Info命令来看看集群效果。

        

        十一、在集群上经过增长数据来测试集群效果。直接看截图效果吧:

        

        每一个Redis的节点都有一个ID值,此ID将被此特定redis实例永久使用,以便实例在集群上下文中具备惟一的名称。 每一个节点都会记住使用此ID的每一个其余节点,而不是经过IP或端口。IP地址和端口可能会发生变化,但惟一的节点标识符在节点的整个生命周期内都不会改变。 咱们简单地称这个标识符为节点ID

4、使用建立群集脚本建立Redis群集

      若是您不想经过如上所述手动配置和执行单个实例来建立Redis群集,则有一个更简单的系统能够代替以上操做(但您不会学到相同数量的操做细节)。

      只需在Redis发行版中检查 utils/create-cluster 目录便可。 里面有一个名为create-cluster的脚本(与其包含的目录名称相同),它是一个简单的bash脚本。 要启动具备3个主站和3个从站的6个节点群集,只需输入如下命令:
        
           一、create-cluster start

           二、create-cluster create

      当redis-trib实用程序但愿您接受集群布局时,在步骤2中回复yes。

      您如今能够与群集交互,默认状况下,第一个节点将从端口30001开始。 完成后,中止群集:

          一、create-cluster stop.

      请阅读此目录中的自述文件以获取有关如何运行脚本的更多信息。


5、测试故障转移

      注意:在此测试期间,应该运行一致性测试应用程序时打开选项卡。

      为了触发故障转移,咱们能够作的最简单的事情(这也是分布式系统中可能发生的语义上最简单的故障)是使单个进程崩溃,在咱们的当前的状况下就是单个主进程。

      咱们能够识别一个集群并使用如下命令将其崩溃:

             $ redis-cli -p 7000 cluster nodes | grep master
             3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385482984082 0 connected 5960-10921
             2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 master - 0 1385482983582 0 connected 11423-16383
             97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422


      好吧,7000,7001和7002都是主服务器节点。 让咱们用 DEBUG SEGFAULT 命令使节点7002崩溃:

           $ redis-cli -p 7002 debug segfault
           Error: Server closed the connection


      如今咱们能够看一致性测试的输出以查看它报告的内容。

        18849 R (0 err) | 18849 W (0 err) |
        23151 R (0 err) | 23151 W (0 err) |
        27302 R (0 err) | 27302 W (0 err) |
        ... many error warnings here ...
   
        29659 R (578 err) | 29660 W (577 err) |
        33749 R (578 err) | 33750 W (577 err) |
        37918 R (578 err) | 37919 W (577 err) |
        42077 R (578 err) | 42078 W (577 err) |


        正如您在故障转移期间所看到的,系统没法接受578次读取和577次写入,可是在数据库中未建立任何不一致。 这听起来可能会出乎意料,由于在本教程的第一部分中,咱们声明Redis群集在故障转移期间可能会丢失写入,由于它使用异步复制。 咱们没有说的是,这种状况不太可能发生,由于Redis会将答复发送给客户端,并将命令复制到从服务器,同时,所以会有一个很是小的窗口来丢失数据。 可是很难触发这一事实并不意味着这是不可能的,因此这不会改变Redis集群提供的一致性保证。

        如今咱们能够检查故障转移后的群集设置(注意,在此期间,我从新启动了崩溃的实例,以便它从新加入做为从属群集):

          $ redis-cli -p 7000 cluster nodes
          3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385503418521 0 connected
          a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385503419023 0 connected
          97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422
          3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385503419023 3 connected 11423-16383
          3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385503417005 0 connected 5960-10921
          2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385503418016 3 connected


        如今,主服务器节点正在端口7000,7001和7002上运行。之前是主服务器节点,即运行在端口7005上的Redis实例,如今是7002的从服务器节点。

            Node ID
          ip:port
          flags: master, slave, myself, fail, ...
          if it is a slave, the Node ID of the master
          Time of the last pending PING still waiting for a reply.
           Time of the last PONG received.
            Configuration epoch for this node (see the Cluster specification).
           Status of the link to this node.
            Slots served...


6、手动故障转移

      有时,强制进行故障转移并不会在主服务器上致使任何问题。例如,为了升级其中一个主节点的Redis进程,最好将其故障转移,以便将其转变为一个对可用性影响最小的从服务器。

      Redis Cluster使用CLUSTER FAILOVER命令支持手动故障转移,该命令必须在要故障转移的主服务器的一个从服务器上执行。

      手动故障转移是比较特殊的,而且与实际主控故障致使的故障转移相比更安全,由于它们是以免数据丢失的方式发生,只有在系统肯定新主服务器节点处理彻底部来自旧主服务器节点的复制流后才将客户从原始主服务器节点切换到新主服务器节点。

      这是您在执行手动故障转移时在从服务器节点的日志中看到的内容:

         #接受用户的手动故障转移请求。
         #已暂停的主服务器手动故障转移接收复制的偏移量:347540
         #处理全部主服务器节点的复制流,手动故障转移能够开始。
         #选举开始延迟0毫秒(等级#0,偏移量347540)。
         #为epoch 7545启动故障转移选举。
         #故障转移选举胜出:我是新主人。

          # Manual failover user request accepted.
         # Received replication offset for paused master manual failover: 347540
         # All master replication stream processed, manual failover can start.
         # Start of election delayed for 0 milliseconds (rank #0, offset 347540).
         # Starting a failover election for epoch 7545.
         # Failover election won: I'm the new master.


      基本上链接到咱们正在故障转移的主服务器节点上的客户端都已中止工做。与此同时,主服务器节点将其复制偏移量发送给从服务器节点,该从服务器节点将等待达到其侧面的偏移量。当达到复制偏移量时,将启动故障转移,并向旧主服务器通知配置开关。 当旧主服务器节点上的客户端被解锁时,它们会被重定向到新主服务器。

7、总结

      今天就写到这里了,关于Cluster的内容尚未写完,有关动态扩容的内容将在下一篇文章作详细介绍。这篇文章对不少东西没有作更细致的探讨,只是从用户的角度来简单说明一下如何搭建Redis的Cluster集群环境。革命还没有成功,我还需努力。我把原文的地址也贴出来,里面的内容不彻底同样,我按着个人理解写的更详细了。地址以下:【Redis cluster tutorial】

相关文章
相关标签/搜索