Redis Cluster集群使用与原理

  • 为什么需要集群
  • 数据分布
    • 分布式数据库-数据分区
    • 哈希分区
      • 节点取余(不建议)
      • 一致性哈希
      • 虚拟槽分区
  • 集群搭建
    • 基本架构
    • Redis Cluster架构
  • Redis Cluster安装
    • 原生命令安装
      • Cluster节点主要配置
    • 官方工具安装集群
      • Ruby环境准备
      • 创建集群
  • 集群伸缩
    • 扩容集群
    • 收缩集群
  • 客户端路由
    • moved重定向
    • ASK重定向
    • moved和ask区别
    • Smart客户端 : 追求性能
      • smart客户端使用-JedisCluster
        • JedisCluster基本使用
        • 多节点命令实现
          • 批量命令实现
        • 四种方案优缺点分析
  • Redis Cluster故障转移
    • 故障发现
    • 故障恢复
  • Redis Cluster开发运维常见问题
    • 集群完整性
    • 带宽消耗
    • Pub/Sub广播
    • 集群倾斜
      • 数据倾斜 : 内存不均
      • 请求倾斜 : 热点
    • 读写分离
    • 数据迁移
    • 集群vs单机
      • 集群限制
      • 思考-分布式Redis不一定好
  • 集群总结

为什么需要集群

  • 并发量QPS较大
  • 数据量较大

高并发和大数据量时, 单机无法满足,这个时候就需要使用分布式

数据分布

分布式数据库-数据分区

Redis Cluster集群使用与原理_第1张图片

  • 顺序分区
  • 哈希分区

Redis Cluster集群使用与原理_第2张图片

顺序分区和哈希分区对比 :

Redis Cluster集群使用与原理_第3张图片

哈希分区

节点取余(不建议)

  • 客户端分片 : 哈希 + 取余

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

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

    Redis Cluster集群使用与原理_第4张图片

一致性哈希

  • 客户端分片 : 哈希 + 顺时针(优化取余)
  • 节点伸缩 : 值影响临近节点, 但还是有数据迁移
  • 翻倍伸缩 : 保证最小迁移数据和负载均衡

Redis Cluster集群使用与原理_第5张图片

虚拟槽分区

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

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

  • 服务端管理节点, 槽, 数据 : 例如Redis Cluster

    Redis Cluster集群使用与原理_第6张图片

集群搭建

基本架构

单机架构 :

Redis Cluster集群使用与原理_第7张图片

分布式架构 :

Redis Cluster集群使用与原理_第8张图片

Redis Cluster架构

  • 节点

  • meet : 节点之间进行通信, 所有节点共享信息

    Redis Cluster集群使用与原理_第9张图片

  • 指派槽

    Redis Cluster集群使用与原理_第10张图片

    客户端与指派槽 :

    Redis Cluster集群使用与原理_第11张图片

    对上面两幅图进行一下说明 :

    1. 将16384个槽分别分配给A, B, C三个节点
    2. 客户端访问时, 根据哈希对槽进行取余,就可以获取到数据存放的槽, 同时也能获取到负责这个数据槽对应的节点
  • 复制

    • 主从复制
    • 高可用
    • 分片

Redis Cluster安装

原生命令安装

  1. 配置开启节点

port ${port}

daemonize yes

dir 工作目录

dbfilename “dump-${port}”.rdb
logfile “\${port}.log

cluster-enabled yes : 配置是否开启cluster

cluster-config-file nodes-${port}.conf : 指定cluster配置文件, 节点启动之后会生成这个文件

  1. meet

    redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7001

    表示从7000 meet 7001

  2. 指派槽

    cluster addslots slot[slot…]

    redis-cli -h 127.0.0.1 -p 7000 cluster addslots {0…5461}, 将0-5461的槽分配给7000节点

  3. 设置主从

    cluster replicate node-id

    redis-cli -h 127.0.0.1 -p 7003 cluster replicate ${node-id-7000}, 表示7003节点去复制7000节点,作为7000节点的从节点

    node-id在节点启动之后生成的cluster配置文件中查看

Cluster节点主要配置

  • cluster-enabled yes : 表示当前节点是Cluster节点
  • cluster-node-timeout 15000 : 故障转移的时间,节点超时的时间,Redis中这个配置有很多用处
  • cluster-config-file “nodes.conf” : 集群节点的配置
  • cluster-require-full-coverage yes : 是否需要集群内所有节点都正常才能提供服务, 默认是yes,通常设置为no

官方工具安装集群

Ruby环境准备

  • 下载, 编译, 安装Ruby

    1. 下载ruby:

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

    2. 解压: tar -zxvf ruby-2.5.1.tar.gz

    3. ./configure -prefix=/usr/local/ruby
    4. make
    5. make install
    6. cd /usr/local/ruby
    7. cp bin/ruby /usr/local/bin
    8. cp bin/gem /usr/local/bin
  • 安装rubygem redis客户端

    1. 下载rubygem redis客户端

      wget https://rubygems.org/downloads/redis-3.3.0.gem

      gem install -l redis-3.3.0.gem

      gem list – check redis gem

  • 安装redis-trip.rb

    拷贝redis-trip.rb脚本到/usr/local/bin目录下

    cp ${REDIS_HOME}/src/redis-trib.rb /usr/local/bin/

创建集群

  • 一键开启

    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

    注: replicas 后面跟的1表示每个节点有几个从节点

集群伸缩

扩容集群

  • 准备新节点

    1. 集群模式
    2. 配置和其他节点统一
    3. 启动后是孤儿节点
  • 加入集群 :

    加入集群-作用 :

    • 为它迁移槽和数据实现扩容
    • 作为从节点负责故障转移

    加入集群方式 :

    • 直接执行meet命令加入集群

    • 使用redis官方提供的redis-trib.rb脚本加入集群

      • redis-trip.rb add-node new_host:new_port existing_host:existing_port –slave –master-id
      • redis-trip.rb add-node 127.0.0.1:6385 127.0.0.1:6379
    • 建议使用redis-trip.rb,能够避免新节点已经加入其他集群,造成故障

  • 迁移槽和数据

    • 槽迁移计划

    • 迁移数据

      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} 命令, 通知槽分配给目标节点

      迁移数据-完整流程图如下:

      Redis Cluster集群使用与原理_第12张图片

    • 添加从节点

收缩集群

Redis Cluster集群使用与原理_第13张图片

  • 下线迁移槽

    Redis Cluster集群使用与原理_第14张图片

  • 忘记节点

    cluster forget {downNodeId}

    Redis Cluster集群使用与原理_第15张图片

  • 关闭节点

客户端路由

moved重定向

Redis Cluster集群使用与原理_第16张图片

槽命中 :

Redis Cluster集群使用与原理_第17张图片

槽未命中 :

Redis Cluster集群使用与原理_第18张图片

ASK重定向

Redis Cluster集群使用与原理_第19张图片

Redis Cluster集群使用与原理_第20张图片

moved和ask区别

  • 两者都是客户单重定向
  • moved : 槽已经确定迁移
  • ask : 槽还在迁移中

Smart客户端 : 追求性能

  1. 从集群中选一个可运行节点, 使用cluster slots初始化槽和节点映射
  2. 将cluster slots的结果映射到本地, 为每个节点创建JedisPool
  3. 准备执行命令, 客户端源码JedisClusterCommand

Redis Cluster集群使用与原理_第21张图片

smart客户端使用-JedisCluster

JedisCluster基本使用

  1. 单例 : 内置了所有节点的连接池
  2. 无需手动借还连接池
  3. 合理设置commons-pool

Redis Cluster集群使用与原理_第22张图片

多节点命令实现

Redis Cluster集群使用与原理_第23张图片

批量命令实现
  1. 串行mget

    Redis Cluster集群使用与原理_第24张图片

  2. 串行IO

    Redis Cluster集群使用与原理_第25张图片

  3. 并行IO

    Redis Cluster集群使用与原理_第26张图片

  4. hash_tag

Redis Cluster集群使用与原理_第27张图片

四种方案优缺点分析

Redis Cluster集群使用与原理_第28张图片

Redis Cluster故障转移

故障发现

  • 通过ping/pong消息实现故障发现 : 不需要sentinel

  • 主观下线和客观下线

    • 主观下线

    定义 : 某个节点认为另一个节点不可用, “偏见”

    主观下线流程 :

    Redis Cluster集群使用与原理_第29张图片

    • 客观下线

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

    客观下线逻辑流程 :

    Redis Cluster集群使用与原理_第30张图片

    尝试客观下线流程 :

    Redis Cluster集群使用与原理_第31张图片

    • 通知集群内所有节点标记故障节点为客观下线
    • 通知故障节点的从节点触发故障转移流程

故障恢复

  • 资格检查

    • 每个从节点检查与故障主节点的断线时间
    • 超过cluster-node-timeout * cluster-slave-validity-factor取消资格
    • cluster-slave-validity-factor : 默认是10
  • 准备选举时间

    Redis Cluster集群使用与原理_第32张图片

  • 选举投票

    Redis Cluster集群使用与原理_第33张图片

  • 替换主节点

    • 当前从节点取消复制变为主节点(slaveof no one)
    • 执行clusterDelSlot撤销故障主节点负责的槽, 并执行clusterAddSlot把这些槽分配给自己
    • 向集群广播自己的pong消息, 表明已经替换了故障从节点

Redis Cluster开发运维常见问题

集群完整性

cluster-require-full-coverage默认为yes

  • 集群中16384槽全部可用 : 保证集群完整性
  • 节点故障或者正在故障转移 : (error)CLUSTERDOWN The cluster is down

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

带宽消耗

Redis Cluster集群使用与原理_第34张图片

  • 消息发送频率 : 节点发现与其它节点最后通信时间超过cluster-node-timeout/2时会直接发送ping消息
  • 消息数据量 : slots槽数组(2KB空间)和整个集群1/10的状态数据(10个节点状态数据约1KB)
  • 节点部署的机器规模 : 集群分布的机器越多且每台机器划分的节点数越均匀, 则集群内整体的可用带宽越高

优化 :

  • 避免”大”集群 : 避免多业务使用一个集群, 大业务可以多集群
  • cluster-node-timeout : 带宽和故障转移速度的均衡
  • 尽量均匀分配到多机器上 : 保证高可用和带宽

Pub/Sub广播

  • 问题 : publish在集群每个节点广播, 加重带宽
  • 解决 : 单独”走”一套Redis Sentinel

集群倾斜

数据倾斜 : 内存不均

  • 节点和槽分配不均
  • 不同槽对应键值数量差异较大
    • CRC16正常情况下比较均匀
    • 可能存在hash_tag
    • cluster countkeysinslot {slot}获取槽对应键值个数
  • 包含bigkey
    • 如大字符中, 几百万元素的hash,set等
    • 从节点使用redis-cli –bigkeys 查找bigkey
    • 优化 : 优化数据结构
  • 内存相关配置不一致
    • hash-max-ziplist-value, set-max-insert-entries等
    • 优化 : 定期”检查”配置一致性

请求倾斜 : 热点

  • 热点key : 重要的key或者bigkey
  • 优化 :
    • 避免bigkey
    • 热键不要用hash_tag
    • 当一致性不高时, 可以用本地缓存 + MQ

读写分离

只读链接 : 集群模式的从节点不接受任何读写请求

  • 对从节点读取时, 会重定向到负责槽的主节点
  • readonly命令可以读 : 连接级别命令

读写分离 : 更加复杂, 不建议集群模式下使用读写分离

  • 同样的问题 : 复制延迟, 读取过期数据, 从节点故障
  • 修改客户端 : cluster slaves {nodeId}

数据迁移

官方迁移工具 : redis-trip.rb import

  • 只能从单机迁移到集群
  • 不支持在线迁移 : source需要停写
  • 不支持断点续传
  • 单线程迁移 : 影响速度

在线迁移 :

  • 唯品会redis-migrate-tool
  • 豌豆荚 : redis-port

集群vs单机

集群限制

  • key批量操作支持有限 : mget, mset必须在一个slot
  • Key事务和Lua支持有限 : 操作的key必须在一个节点
  • key是数据分区的最小粒度 : 不支持bigkey分区
  • 不支持多个数据库 : 集群模式下只有一个db 0
  • 复制只支持一层 : 不支持树形复制结构

思考-分布式Redis不一定好

  1. Redis Cluster : 满足容量和性能的扩展性, 很多业务”不需要”
    • 大多数时客户端性能会”降低”
    • 命令无法跨节点使用 : mget, keys, scan, flush, sinter等
    • Luau和事务无法跨节点使用
    • 客户端维护更复杂 : SDK和应用本身销毁
  2. 很多场景Redis Sentinel已经足够好

集群总结

  • Redis cluster数据分区规则采用虚拟槽方式(16384个槽), 每个节点负责一部分槽和相关数据, 实现数据和请求的负载均衡
  • 搭建集群划分四个步骤 : 准备节点, 节点握手, 分配槽, 复制.redis-trip.rb工具用于快速搭建集群.
  • 集群伸缩通过在节点之间移动槽和相关数据实现
    • 扩容时根据槽迁移计划把槽从源节点迁移到新节点
    • 收缩时如果下线的节点有负责的槽需要迁移到其他的节点, 再通过cluster forget命令让集群内所有节点忘记被下线节点
  • 使用smart客户端操作集群达到通信效率最大化, 客户端内部负责计算维护键->槽->节点的映射, 用于快速定位到目标节点
  • 集群自动故障转移过程分为故障发现和节点恢复. 节点下线分为主观下线和客观下线, 当超过半数节点认为故障节点为主观下线是标记它为客观下线状态.从节点负责对客观下线的主节点触发故障恢复流程, 保证集群的可用性.
  • 开发运维常见问题 : 超大规模集群带宽消耗, pub/sub广播问题, 集群倾斜问题, 单机和集群对比等

你可能感兴趣的:(Redis,中间件,分布式,Redis专栏)