Redis Cluster

Redis集群

当我们的单台的机器无法满足生产中的实际需求的时候,我们就需要一种能够支撑我们业务运行的模式,所以集群的因此诞生。为构建大数据量的信息处理提供便捷的服务。

为什么需要集群呢?

  • 1.redis可以提供 –>10万/s的执行速度

  • 2.redis需要的内存 –> 机器内存16~256G

关于集群的需求

  • 并发量:OPS

  • 数据量:大数据

解决的方案

  • 配置”最强悍”的机器,超大的内存,最牛逼的CPU(单节点)

最优的解决方案

  • 分布式:简单的理解就是加大量的机器(Redis 3.0提供)

数据分布

常用的分区方式

  • 顺序分布
  • 哈希分布(点的取模)
    • 节点取余分区
    • 一致性哈希分区
    • 虚拟槽分区
  • 数据分布对比图

  • 当业务提升时候需要添加一个节点

  • 多倍扩容
  • 节点取余
    • 客户端分片:哈希+取余

    • 节点伸缩:数据节点关系变化,导致数据迁移

    • 迁移数量和添加节点数量有关:建议节点翻倍

  • 一致性哈希

    • 客户端分片:哈希 + 顺时针(优化取余)

    • 节点伸缩:只影响邻近节点,但是还是有数据的迁移

    • 翻倍伸缩:保证最小迁移数据和负载均衡

  • 虚拟槽分区
    • 预设虚拟槽:每个槽映射一个数据子集,一般比节点数大

    • 良好的哈希函数:例如CRC16

    • 服务端的管理节点、槽、数据:例如Redis cluster

搭建集群

Redis-Cluster分布式架构

Redis-Cluster基本架构

  • 节点
    • cluster-enable=yes
  • meet(通讯知道指派槽的位置)

  • 指派槽

  • 复制
    • 每一个主节点都有一个从节点
    • 高可用
    • 分片(多个节点进行读写)

Redis-Cluster两种安装方式

  • 原生命令安装

  • 官方工具安装(Ruby)

原生命令安装-理解架构

  • 1.配置开启节点

  • 2.Meet(握手)

  • 3.指派槽

  • 4.主从

配置开启Redis

  • 6台redis集群

配置文件

port {port} daemonize yes   #守护进程 dir "路径"    #目录存放的地址 dbfilename "dump-{port}.rdb" 
logfile "{port}.log"   #日志文件命名 cluster-enabled yes #开启集群 cluster-config-file node-{port}.conf   #集群文件存放位置
cluster-require-full-coverage no    #是否所有的节点正常才提供服务

节点的握手

运行节点并查看

for i in {1..5};do sed  "s/7000/700i/g" redis-7000.conf > redis-700i.conf;redis-server redis-700$i.conf;done  #启动全部的redis节点
netstat -lnput |grep redis-server

for i in {1..5};do redis-cli -p 7000 cluster meet 127.0.0.1 700i;redis-cli -p 700i cluster nodes;done     #指定集群节点
redis-cli -p 7000 cluster  nodes        #查看集群状态

分配槽

分配槽的计划

  • 16384/3
节点 槽数
7000 0~5461
7001 5462~10922
7002 10923~16383

编写脚本手动分配槽

#!/bin/bash 
start=1    #开始的槽位置 end=2  #结束的槽位置
port=3 #redis的端口号 for i in `seq {start} {end}` do      echo "slot:{i}"
    redis-cli -p {port} cluster addslots {i} 
done

主从的分配

Master slave
7000 7003
7001 7004
7002 7005
redis-cli -p node_slave cluster replicate node_master_id    #设置主从
redis-cli -p 7000 cluster nodes

查看的当前的槽的分配情况

redis-cli -p 7000 cluster slots

验证集群是否成功

[root@docker script]# redis-cli -c -p 7000
127.0.0.1:7000> set hello word
OK
127.0.0.1:7000> 
  • 实际生产中不会这样使用,本次仅仅是实验

生产中使用Ruby构建环境

Ruby环境的准备

  • 下载、编译、安装Ruby

  • 安装Rubygem redis

  • 安装redis-trib.rb

安装Ruby

下载Ruby

wget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.1.tar.gz

安装Ruby

tar -xf ruby-2.5.1.tar.gz
./configure --prefix=/usr/local/ruby
make && make install
cd /usr/local/ruby/
cp bin/ruby /usr/local/bin/
cp bin/gem /usr/local/bin/

安装rubygem redis

gem install redis       #(等与python pip)

安装redis-trib.rb

cp /root/redis-4.0.9/src/redis-trib.rb /usr/local/bin

启动redis-trib.rb

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

redis-cli -p 7000 cluster info

总结

  • 1->原生命令的安装
    • 理解Redis Cluster架构
    • 生产环境中不使用
  • 2->官方工具安装
    • 高效、准确
    • 生产环境中常使用
  • 3-> 其他
    • 可视化部署

集群伸缩

  • 为了它迁移槽和数据时间扩容

  • 作为从节点负责故障转移

伸缩原理

  • 集群伸缩 = 槽和数据在节点之间的移动

## 扩容集群

  • 准备新的节点
    • 集群模式

    • 配置和其他节点统一

    • 启动后是孤儿节点

redis-server redis-8000.conf    #启动节点
redis-server redis-8001.conf    #启动节点

扩容的原理

redis cluster 水平扩展原理

redis4.0 cluster支持指定slot在节点中移动,也支持加入空节点后根据集群节点中已存在的slot分布自动进行再分布。以redis-trib的move_slot为例解析slot移动的过程
步骤1): 调用setslot命令修改源、目标节点slot的状态
步骤2): 获取源节点上slot的key列表
步骤3): 调migrate命令迁移key,迁移过程中redis属于阻塞状态,只有目标节点restore成功后才返回
步骤4): 调用setslot命令修改源、目标节点slot的状态

在迁移过程中,如何保证数据的一致性呢?

redis cluster提供迁移状态中的重定向机制,向客户端返回ASK,客户端收到后需先发送asking指令到目标节点上,然后再发请求到目标节点上才可以访问。当访问的key满足以下全部条件时会出现重定向返回
key所属slot在该节点上,如不在,返回的是MOVE
slot处于迁移状态中
key不存在
如上所述,migrate 是一个同步阻塞型的操作,如果key并不为空,即使slot处于迁移状态,key依然能被读写,以此保证数据的一致性。

加入集群

  • 迁移槽和数据

手工的方式迁移

redis-cli -p 7000 
    127.0.0.1:7000> cluster meet 127.0.0.1 8000
    127.0.0.1:7000> cluster meet 127.0.0.1 8001
redis-cli -p 7000 cluster nodes #查看集群信息
#现在加入的节点是孤儿节点,无法进行集群,需要手动去设置,详情看之前的手动版集群。

官方提供的Ruby加入节点的方式

  • 建议使用官方的redis-trib.rb能够有效的避免新的节点已经加入了其他的集群,造成故障
    redis-trib.rb add-node new_host:new_port existing_host:existing_port --slave --master-id <arg>      #使用语法
    redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7007

设置主从并查看状态

redis-cli -p 7007 cluster replicate 5a5c1f55bf1a9b81369a94c8213bc3054d655a42
redis-cli -p 7000 cluster nodes

迁移槽的计划

  • 槽的迁移

  • 配给目标节点。

  • 添加从节点

迁移数据

官方办法

  • 1.对目标节点发送:cluster setslot {slot} importing {sourceNodeId}命令,让目标节点准备导入槽的数据

  • 2.对源节点发送:cluster setslot {slot} migrating {targetNodeId}命令,让源节点准备迁移出槽的数据

  • 3.源节点循环执行cluster getkeysinslot {slot} {count}命令,每一次获取count个属于槽的健

  • 4.在源节点上执行migrate {targetIp} {targetPort} key 0 {timeout} 命令把指定key迁移

  • 5.重复执行步骤3~4直到槽下面的所有数据迁移到目标节点中

  • 6.向集群内所有节点发送cluster setslot {slot} node {targetNodeId}命令,通知槽分

Ruby管理方式

  • 第一步想要迁移的槽的数量

  • 第二步想迁移到哪一个节点ID

  • 第三步是否全部迁移

  • 第四步是否想要继续这个过程

  • 第五步等待自动迁移查看结果

迁移过程

redis-trib.rb  reshard 127.0.0.1:7000   #进行数据的迁移,根据提示进行

 redis-cli -p 7000 cluster nodes

收缩集群

  • 下线迁移槽

  • 忘记节点

  • 关闭节点

下线迁移槽

  • 查看要下线节点的所有额槽的区间对应的节点,逐一迁移到对应的节点上去

  • redis-trib.rb reshard –from 迁移源节点ID to 迁移目标ID slots 迁移的槽的数量 执行IP:port

执行三个节点的槽迁移

# 7006 --> 7000 槽数量1366
redis-trib.rb reshard --from 5a5c1f55bf1a9b81369a94c8213bc3054d655a42 --to 6cff7c04ff00f7920edef7dffe0ac9ce3d6696ce  --slots 1366 127.0.0.1 7006
# 7006 --> 7001 槽数量1365
redis-trib.rb reshard --from 5a5c1f55bf1a9b81369a94c8213bc3054d655a42 --to 378adc2f1d1056972c51ba660cd35f722ef17e90  --slots 1365 127.0.0.1:7006
# 7006 --> 7002 槽数量1365
redis-trib.rb reshard --from 5a5c1f55bf1a9b81369a94c8213bc3054d655a42 --to e2957fb3cabf1c83f0d613eda31aed1741c015f5   --slots 1365 127.0.0.1:7006
#保证完全把要下线的节点的槽清理空

忘记节点

  • 节点下线原则:
    • 先下线从节点后下线主节点,否则会触发故障迁移

redis-cli -p port cluster forget {downNodeId}   #忘记节点(官方版)

redis-trib.rb del-node  127.0.0.1:7007 88162e6f4152ea820b2ff4d4ca2c37c534b10286
redis-trib.rb del-node  127.0.0.1:7006 5a5c1f55bf1a9b81369a94c8213bc3054d655a42 
#查询节点7006和7007均下线

客户端路由

  • moved重定向

  • ask重定向

  • smart重定向

moded

槽命中:直接返回

槽不名中:moved异常

redis-cli -c -p 端口 以clsuter模式进行

ask

产生的背景

解决的方案

moved和ask

  • 两者都是客户单重定向

  • moved:槽已经确定迁移

  • ask:槽还在迁移的过程中

smart

  • 高效合理的客户端设计

  • 追求极大的性能

    • 1.从集群中选择一个可以运行的节点,使用cluster slots初始化槽和节点映射
    • 2.将cluster slots的结果映射到本地,为每一个节点创建Jedispool
    • 3.执行命令

批量优化的方法

  • 1.串行mget

  • 2.串行I/O

  • 3.并行I/O

  • 4.hash_tag

四种方式的对比

故障转移

  • 故障的发现

  • 故障恢复

  • 故障演练

故障的发现

  • 通过ping/pang消息实现故障发现(不使用sentinel)

  • 主观下线和客观下线

主观下线的流程

  • 主观下线
    • 定义:某一个节点认为另外一个节点不可用,“偏见”。

客观下线的流程

  • 客观下线
    • 定义:当半数以上持有槽的主节点都标记某节点主观下线

下线的逻辑流程

客观下线流程

  • 通知集群内的所有节点标记故障节点为客观下线

  • 通知故障节点的从节点触发故障转移流程

故障恢复

  • 1 – > 资格检查

  • 2 – > 准备选举时间

  • 3 – > 选举投票

  • 4 – > 替换主节点

检查资格

  • 每个从节点检查与故障节点的断线时间

  • 超过cluster-node-timeout * cluster-slave-validity-factor取消资格

  • cluster-slave-validity-factor 默认为10

主节点选举

替换主节点

  • 当前从节点取消复制变为主节点(slaveof no one)

  • 执行clusterDelSlot撤销故障主节点负责的槽,并执行clusterAddSlot把这些槽分配给自己

  • 向集群广播自己的Pong消息,表明已经替换了故障从节点

故障模拟

手动的停止master节点,查看故障转移的过程

主动恢复master节点
并且查看相应的日志即可

redis-server redis-7000.conf

运维开发常见的问题

  • 集群的完整性

  • 带宽消耗

  • PUB/SUB广播

  • 数据倾斜

  • 读写分离

  • 数据迁移

  • 集群单机

集群的完整性

  • cluster-require-full-coverage默认为yes

    • 集群中16384个槽全部可用(保证集群的完整性)

    • 节点故障或者正在故障转移(err -> clusterdown the cluster is down)

  • 大多数的业务无法容忍(cluster-require-full-coverage建议设置为no)

开启新的6个redis节点

for i in {1..5};do sed "s/no/yes/g;s/7000/900i/g" redis-9000.conf > redis-900i.conf; redis-server redis-900$i.conf ;done;sed -i "s/7/9/g;s/no/yes/g" redis-9000.conf;redis-server redis-9000.conf ;ps -ef |grep redis

带宽消耗

  • 官方建议:1000节点

  • PING//PONG消息

  • 不容忽视的带宽消耗

三个方面

  • 消息发送的频率:节点发现与其他节点最后通讯时间超过cluster-node-timeout/2时会直接发送ping消息

  • 消息数据量:slots槽数组(2KB空间)和整个集群1/10的状态数据(10个节点状态数据约为1KB)

  • 节点部署的机器规模:集群分布的机器越多且每一台机器划分的节点数越均匀,则集群内整体可用的带宽越高

一个例子

  • 规模:节点200个,20台物理机器(每台10个节点)

  • cluster-node-timeout = 15000 , ping/pong带宽为25MB

  • cluster-node-timeout = 20000 , ping/pong带宽低于15MB

相关优化

  • 避免大集群,避免多业务使用一个集群,大业务可以多个集群

  • cluster-node-timeout:带宽和故障转移的速度的均衡

  • 尽量均匀分配到多台机器上:保证高可用和带宽

Pub/Sub广播

  • 问题: publish在集群每一个节点广播加重带宽使用

  • 解决:单独走一套Redis Sentinel

集群倾斜

  • 数据倾斜:内存不足

  • 热点问题

倾斜原因

  • 节点和槽的分配不均匀
    • redis-trib.rb info ip:port 查看节点和槽和键值分布

    • redis-trib.rb rebalance ip:port进行均衡(谨慎使用)

  • 不同槽对应键值数量的差异比较大

    • CRC16正常情况下比较均匀

    • 可能个存在hash_tag

    • cluster countkeysinslot {slot}获取槽对应键值个数

  • 包含bigkey

    • bigkey: 例如大字符串几百万的元素的hash set

    • 从节点: redis-cli –bigkeys

    • 优化: 优化数据结构

  • 内存相关配置不一致

    • 优化: 定期 “检查” 配置一致性

Redis Cluster》有1个想法

发表评论

电子邮件地址不会被公开。 必填项已用*标注