转载:https://blog.csdn.net/love905661433/article/details/82317635#思考-分布式redis不一定好
2018年09月02日 18:11:29 七夜丶雪 阅读数 572更多
所属专栏: Redis专栏
版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/love905661433/article/details/82317635
为什么需要集群
- 并发量QPS较大
- 数据量较大
高并发和大数据量时, 单机无法满足,这个时候就需要使用分布式
数据分布
分布式数据库-数据分区
- 顺序分区
- 哈希分区
顺序分区和哈希分区对比 :
哈希分区
节点取余(不建议)
-
客户端分片 : 哈希 + 取余
-
节点伸缩 : 数据节点关系变化, 导致数据迁移
-
迁移数量和添加节点数量有关 : 建议翻倍扩容
一致性哈希
- 客户端分片 : 哈希 + 顺时针(优化取余)
- 节点伸缩 : 值影响临近节点, 但还是有数据迁移
- 翻倍伸缩 : 保证最小迁移数据和负载均衡
虚拟槽分区
-
预设虚拟槽 : 每个槽映射一个数据子集, 一般比节点数大
-
良好的哈希函数 : 例如CRC16
-
服务端管理节点, 槽, 数据 : 例如Redis Cluster
集群搭建
基本架构
单机架构 :
分布式架构 :
Redis Cluster架构
-
节点
-
meet : 节点之间进行通信, 所有节点共享信息
-
指派槽
客户端与指派槽 :
对上面两幅图进行一下说明 :
- 将16384个槽分别分配给A, B, C三个节点
- 客户端访问时, 根据哈希对槽进行取余,就可以获取到数据存放的槽, 同时也能获取到负责这个数据槽对应的节点
-
复制
- 主从复制
- 高可用
- 分片
Redis Cluster安装
原生命令安装
- 配置开启节点
port ${port}
daemonize yes
dir 工作目录
dbfilename “dump-${port}”.rdb
logfile “\${port}.logcluster-enabled yes : 配置是否开启cluster
cluster-config-file nodes-${port}.conf : 指定cluster配置文件, 节点启动之后会生成这个文件
-
meet
redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7001
表示从7000 meet 7001
-
指派槽
cluster addslots slot[slot…]
redis-cli -h 127.0.0.1 -p 7000 cluster addslots {0…5461}, 将0-5461的槽分配给7000节点
-
设置主从
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
-
下载ruby:
wget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.1.tar.gz
-
解压: tar -zxvf 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客户端
-
下载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表示每个节点有几个从节点
集群伸缩
扩容集群
-
准备新节点
- 集群模式
- 配置和其他节点统一
- 启动后是孤儿节点
-
加入集群 :
加入集群-作用 :
- 为它迁移槽和数据实现扩容
- 作为从节点负责故障转移
加入集群方式 :
-
直接执行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,能够避免新节点已经加入其他集群,造成故障
-
迁移槽和数据
-
槽迁移计划
-
迁移数据
-
对目标节点发送 : cluster setslot {slot} importing {sourceNodeId}命令, 让目标节点准备导入槽的数据.
-
对源节点发送 : cluster setslot {slot} migrating {targetNodeId}命令, 让源节点准备迁出槽的数据.
-
源节点循环执行 : cluster getkeysinslot {slot} {count}命令, 每次获取count个属于从槽的键.
-
在源节点上执行 : migrate {targetIP} {targetPort} key 0 {timeout} 命令把指定的key迁移.
-
重复执行步骤3~4直到槽下所有的键数据迁移到目标节点
-
向集群内所有主节点发送cluster setslot {slot} node {targetNodeId} 命令, 通知槽分配给目标节点
迁移数据-完整流程图如下:
-
-
添加从节点
-
收缩集群
-
下线迁移槽
-
忘记节点
cluster forget {downNodeId}
-
关闭节点
客户端路由
moved重定向
槽命中 :
槽未命中 :
ASK重定向
moved和ask区别
- 两者都是客户单重定向
- moved : 槽已经确定迁移
- ask : 槽还在迁移中
Smart客户端 : 追求性能
- 从集群中选一个可运行节点, 使用cluster slots初始化槽和节点映射
- 将cluster slots的结果映射到本地, 为每个节点创建JedisPool
- 准备执行命令, 客户端源码JedisClusterCommand
smart客户端使用-JedisCluster
JedisCluster基本使用
- 单例 : 内置了所有节点的连接池
- 无需手动借还连接池
- 合理设置commons-pool
多节点命令实现
批量命令实现
-
串行mget
-
串行IO
-
并行IO
-
hash_tag
四种方案优缺点分析
Redis Cluster故障转移
故障发现
-
通过ping/pong消息实现故障发现 : 不需要sentinel
-
主观下线和客观下线
- 主观下线
定义 : 某个节点认为另一个节点不可用, “偏见”
主观下线流程 :
- 客观下线
定义 : 当半数以上持有槽的主节点都标记某节点主观下线
客观下线逻辑流程 :
尝试客观下线流程 :
- 通知集群内所有节点标记故障节点为客观下线
- 通知故障节点的从节点触发故障转移流程
故障恢复
-
资格检查
- 每个从节点检查与故障主节点的断线时间
- 超过cluster-node-timeout * cluster-slave-validity-factor取消资格
- cluster-slave-validity-factor : 默认是10
-
准备选举时间
-
选举投票
-
替换主节点
- 当前从节点取消复制变为主节点(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
带宽消耗
- 消息发送频率 : 节点发现与其它节点最后通信时间超过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不一定好
- Redis Cluster : 满足容量和性能的扩展性, 很多业务”不需要”
- 大多数时客户端性能会”降低”
- 命令无法跨节点使用 : mget, keys, scan, flush, sinter等
- Luau和事务无法跨节点使用
- 客户端维护更复杂 : SDK和应用本身销毁
- 很多场景Redis Sentinel已经足够好
集群总结
- Redis cluster数据分区规则采用虚拟槽方式(16384个槽), 每个节点负责一部分槽和相关数据, 实现数据和请求的负载均衡
- 搭建集群划分四个步骤 : 准备节点, 节点握手, 分配槽, 复制.redis-trip.rb工具用于快速搭建集群.
- 集群伸缩通过在节点之间移动槽和相关数据实现
- 扩容时根据槽迁移计划把槽从源节点迁移到新节点
- 收缩时如果下线的节点有负责的槽需要迁移到其他的节点, 再通过cluster forget命令让集群内所有节点忘记被下线节点
- 使用smart客户端操作集群达到通信效率最大化, 客户端内部负责计算维护键->槽->节点的映射, 用于快速定位到目标节点
- 集群自动故障转移过程分为故障发现和节点恢复. 节点下线分为主观下线和客观下线, 当超过半数节点认为故障节点为主观下线是标记它为客观下线状态.从节点负责对客观下线的主节点触发故障恢复流程, 保证集群的可用性.
- 开发运维常见问题 : 超大规模集群带宽消耗, pub/sub广播问题, 集群倾斜问题, 单机和集群对比等