Redis集群

发布时间 2023-12-15 21:01:14作者: _mcj

1.描述

集群,即是Redis Cluster。其由多个redis节点组成,redis数据保存在这些节点中。这些节点分为主节点和从节点:只有主节点负责读写请求和集群信息的维护,从节点只负责主节点数据和状态的复制。

2.作用

  • 数据分区:redis集群是将数据分散存到多个节点中的。具体存到哪个节点是根绝数据的key通过CRC16计算得出结果后再模16384得出存在哪个哈希槽中,再看该哈希槽再哪个节点上就将该数据存到哪个节点。
  • 高可用:redis集群支持主从复制和故障节点自动转移,这样当任一节点发生故障后,集群仍然能够对外提供服务。

3.搭建

redis集群的搭建大致可以分为两种方式,1:手动执行redis命令。2:使用Ruby脚本搭建。我是使用手动搭建的方式,搭建一个三主三从的集群。
  手动搭建集群的可以分为四步:1:启动节点:此时节点还是独立的,并没有建立联系;2:节点握手:让每个节点之间建立联系;3:分配槽:将16384个槽分配给主节点;4:指定主从关系:为每个主节点指定从节点

3.1 启动节点

主备好三主三从节点的配置文件,其中6380/6381/6382是主,6390/6391/6392是从。

#redis-6380.conf
port 6380
cluster-enabled yes
cluster-config-file "node-6380.conf"
logfile "log-6380.log"
dbfilename "dump-6380.rdb"

#redis-6381.conf
port 6381
cluster-enabled yes
cluster-config-file "node-6381.conf"
logfile "log-6381.log"
dbfilename "dump-6381.rdb"

#redis-6382.conf
port 6382
cluster-enabled yes
cluster-config-file "node-6382.conf"
logfile "log-6382.log"
dbfilename "dump-6382.rdb"

#redis-6390.conf
port 6390
cluster-enabled yes
cluster-config-file "node-6390.conf"
logfile "log-6390.log"
dbfilename "dump-6390.rdb"

#redis-6391.conf
port 6391
cluster-enabled yes
cluster-config-file "node-6391.conf"
logfile "log-6391.log"
dbfilename "dump-6391.rdb"

#redis-6392.conf
port 6392
cluster-enabled yes
cluster-config-file "node-6392.conf"
logfile "log-6392.log"
dbfilename "dump-6392.rdb"

写好配置文件后,通过redis-server命令启动所有的节点:

redis-server redis-6380.conf
redis-server redis-6381.conf
redis-server redis-6382.conf
redis-server redis-6390.conf
redis-server redis-6391.conf
redis-server redis-6392.conf

3.2 节点握手

由于刚启动的节点都是处于独立状态,所以需要通过节点握手操作将他们进行联系起来。节点握手时通过命令cluster meet <ip> <port>来实现的。关于这里的ip是为了其它机器上的节点和客户端也可以访问使用的是局域网的ip,由于我这边演示是在一台电脑上进行演示,因此ip就直接使用127.0.0.1了。
登录6380节点执行如下命令:

cluster meet 127.0.0.1 6381
cluster meet 127.0.0.1 6382
cluster meet 127.0.0.1 6390
cluster meet 127.0.0.1 6391
cluster meet 127.0.0.1 6392

执行完成之后通过cluster nodes来查看节点的信息如下:
image
从图中可以看到六个节点都已经联系起来了。

3.3 分配槽

刚联系起来的节点由于16384个槽还没有分配,因此该集群还是未启动状态,可通过cluster info命令查看。结果如下:
image
为每个节点分配槽可以通过redis-cli -h <ip> -p <port> cluster addslots {开始槽..结束槽}命令来进行为节点分配槽。执行如下命令:

redis-cli -h 127.0.0.1 -p 6380 cluster addslots {0..5461}
redis-cli -h 127.0.0.1 -p 6381 cluster addslots {5462..10922}
redis-cli -h 127.0.0.1 -p 6382 cluster addslots {10923..16383}

由于我这用的是windows系统,会一直报错:
image
至于这个错误的原因个人认为原因是因为windows系统不能展开{0..5461}。我试过分配具体的槽节点是可以成功的。至于windows系统如何批量分配大量的接口目前还不知道。

3.4 指定主从关系

集群中指定主从关系不是使用slaveof命令,而是使用redis-cli -h <ip> -p <port> cluster replicate <主节点id>命令。执行命令如下:

redis-cli -h 127.0.0.1 -p 6390 cluster replicate 24f5e1439c719c526db96c05bc0b0b56d96a60fb
redis-cli -h 127.0.0.1 -p 6391 cluster replicate c7fadac24cce763ad7b080658d2f7d9a90010ed6
redis-cli -h 127.0.0.1 -p 6392 cluster replicate 516e0db2955a9c6526371de77679c5b9c6dc24b5

执行之后为:
image
可以看到6390/6391/6392此时已是6380/6381/6382的从节点。

4.一些说明

4.1 不能保证强一致性

redis集群并不能保证数据的强一致性,会在某些不确定的情况下丢失:

  • 主从节点之间采用了异步的方式进行同步数据
    由于主节点再接收到客户端的写操作之后,其是先回复客户端写操作成功之后,随后再同步数据到它的从节点。如果在回复客户端之后,同步数据给从节点之前主节点发生故障,那么它的一个从节点晋升为主节点,那么此时的数据就丢失了。
  • 网络问题
    如果网络出现问题,将一个集群分为了两组网络,两组网络互不连通。例如有A,B,C,A1,B1,C1集群,其中A,B,C是主节点,A1,B1,C1是从节点,如果A,B,A1,B1,C1在一组网络,C在另一组网络,如果客户端连接的为C,写入的数据也在C节点上,如果网络异常的时间足够长,此时C1已经被选为主节点了,那么在网络恢复后,客户端写道C节点上的数据就会丢失。

4.2 参数配置

  • cluster-enabled <yes/no>:集群功能是否开启,yes开启,no关闭。
  • cluster-config-file <filename>:集群配置文件,但是不能人工编辑,是集群节点自动维护的文件,用于记录集群中有哪些节点,节点的状态与持久化参数。在收到请求后更新。
  • cluster-node-timeout <milliseconds>:集群节点能失联的最大时间,超过这个时间,则认为该节点故障。如果主节点失联超过这个时间,则进行故障转移,将从节点升级为主节点。
  • cluster-slave-validity-factor <factor>:如果设置为0,则无论从节点与主节点失联多久,从节点都会尝试升级为主节点。如果设置为整数,则cluster-node-timeout乘以cluster-slave-validity-factor得到的时间,是从节点与主节点失联后,此从节点数据有效的最长时间,超过这个时间,则此从节点不会启动故障迁移。需要注意的是如果该参数设置为非0,那么可能导致主节点故障后,没有从节点升级为主节点的问题,只有主节点重新回归,集群才能恢复运作。
  • cluster-migration-barrier <count>:主节点需要的最小从节点数,只有主节点有该数量正常工作的从节点,那么其才会分配多余的从节点给其它从节点数量不够主节点。例如该参数设置为1,此时有两个主节点A,B,此时A有从节点A1。B没有从节点,此时再重新启动一个从节点B1分配给A节点,此时check后会发现B1从节点是在B这个主节点下。
  • cluster-require-full-coverage <yes/no>:在部分哈希槽所在的节点不可用时,如果此参数设置为yes,则整个集群停止接受操作;如果参数设置为no,则集群依然为可达节点上的哈希槽提供读操作。默认值为yes。

4.3 数据分区方式

redis的数据分区方式有三种:分别是逻辑拆分,顺序分区和哈希分区。

  • 逻辑拆分
    是指按照逻辑上进行分别存储数据。比如:一个业务的数据全部存到A节点,而另一个业务的数据则全部存到B节点。
  • 顺序分区
    将一整块数据分散到很多机器中。比如将份数据分为三块,一块存到A,一块存到B,一块存到C中
  • 哈希分区
    哈希分区又有三种方式,分别是哈希取余分区,一致性哈希分区,哈希槽分区。
    (1)哈希取余分区
    是指首先对数据的key进行hash取值,得到值后对节点数量进行取模,最后得到的值就决定该数据映射到哪个节点上。其缺点是当节点的数量变化时(增加或减少),那么几乎所有的数据都要重新计算新的映射关系。
    (2)一致性哈希分区
    一致性哈希是指将整个哈希值空间(一般为0~2^32-1,因为一致性hash算法是用来做服务器的负载均衡,而服务器的ip地址为32位)组成一个虚拟的圆,先将服务器进行哈希,确定每个服务器在圆环上的位置。再将值进行哈希,得到哈希值后按照顺时针的方向走,遇到的第一台服务器就是该数据要映射到的服务器。其相比哈希取余分区的优点是节点数量的增加或减少只会影响到相邻的节点。缺点是当系欸但数量较少时,增加或减少节点对单个节点的影响可能回很大。
    (3)哈希槽分区
    此方案是在一致性哈希分区的基础上引入了一个槽的概念。数据计算key的哈希值后将其映射到槽上,再将槽映射到节点上。此时通过槽进行数据管理和迁移,相当于槽解耦了节点和数据之间的关系。
    哈希槽的计算方式为CRC16(key) % 16384;这里对16384取余是因为redis集群的哈希槽的数量为16384个;
    为什么CRC16算法可以产生2^16=65536个值,而redis集群的哈希槽只有16384个:这是因为redis集群每秒都会发送一定数量的ping/pong消息用于心跳检测,而消息头中有一个myslots[CLUSTER_SLOTS/8]的char数组,其每一位代表一个槽,如果该位为1,并表示这个槽属于该节点。其占用的大小为槽数量/8/1024,如果槽位为65536,则其占用8K,就会导致发送的消息过于庞大,浪费带宽;同时redis集群节点数量一般不超过1000个,节点越过,消息体携带的数据越多,如果节点超过1000个也会导致网络拥堵。而对于1000个以内的节点,16384个槽位已经够用了;另一个就是槽位越小,节点少的情况下压缩比比较高。redis的哈希槽信息是通过bitmap的形式存储的,bitmap的填充率越小,其压缩比越高,而填充率的计算方法为:槽数量/节点数量

4.4 端口说明

每个redis集群节点都需要提供两个TCP端口:

  • 普通端口:就是我们提供给客户端连接的端口,比如6379等。
  • 集群端口:端口号是普通端口加10000(固定值,无法改变),如6379的集群端口就是16379,其只用于节点之间的通信,如故障转移等。
    ps:配置防火墙是需要将普通端口和集群端口都开启

4.5 消息类型

redis集群节点之间发送的消息类型主要有五种:meet消息、ping消息、pong消息、fail消息、publish消息。

  • meet消息:在节点握手阶段,当节点受到客户端的cluster meet命令时,会向新加入的节点发送meet消息,请求新节点加入到当前的集群;新节点受到消息后回复一个pong消息
  • ping消息:集群中的每个节点每秒钟会选择部分节点发送ping消息,接收者回复一个pong消息。用于检测节点状态,交换彼此信息。发送的规则为:(1)随机找5个节点,在其中选择最久一个没有通信的节点。(2)扫描节点列表,选择最近一次受到pong消息时间大于cluster_node_timeout/2的所有节点。
  • pong消息:封装了自身的状态数据,当收到meet消息或ping消息时回复pong消息,还有一种是向集群广播pong消息。比如故障恢复后新的主节点会广播pong消息。
  • fail消息:当一个主节点判断另一个主节点进入fail状态时,会向集群广播这一fail消息。接收节点会将该fail消息把偶才能,便于后续的判断。
  • publish消息:节点在收到publish命令后,会先执行该命令,然后向集群广播这一消息,接收节点也会执行该publish命令。

5.总结

  redis集群主要是能够数据分区,突破了单机redis内存大小的限制,使得存储容量大大增加;同时集群支持主从复制和主节点的故障转移,因此其同样有高可用性。
  redis的数据分区方式使用的是哈希槽分区的方式,其解耦了数据与节点之间的关系,使得节点的添加与减少对系统的影响很小。