redis-imooc2笔记
redis-sentinel
三个定时任务
- 每10s每个sentinel对master和slave执行info
- 发现slave节点
- 确认主从关系
- 每2s每个sentinel通过master节点的channel交换信息(pub/sub)
- 通过_sentinel_:hello频道交互
- 交互对节点的“看法”和自身信息
- 每1s每个sentinel对其他的sentinel和redis执行ping
- 心跳检测,失败判定依据
主观下线和客观下线
- sentinel monitor (quorum最好取redis节点数/2加一)
- sentinel down-after-milliseconds
- 主观下线:每个sentinel节点对redis节点失败的“偏见”
- 客观下线:所有sentinel节点对redis节点失败“达成共识”(超过quorum个数统一)
- sentinel is-master-down-by-addr
领导者选举
- 原因:只有一个sentinel节点完成故障转移
- 选举:通过sentinel is-master-down-by-addr命令都希望成为领导者
- 每个做主观下线的sentinel节点向其他sentinel节点发送命令,要求将他设置为领导者
- 收到命令的sentinel节点如果没有同意通过其他sentinel节点发送的命令,那么将同意该请求否则拒绝
- 如果该sentinel节点发现自己的票数已经超过sentinel集合半数且超过quorum,那么它将成为领导者
- 如果此过程有过个sentinel节点成为领导者,那么将等待一段时间后重新进行选举
- 故障转移(sentinel领导者节点完成)
- 从slave节点中选出一个“合适的”节点作为新的master节点
- 对上面的slave节点执行slaveof no one 命令让其成为master节点
- 向剩余的slave节点发送命令,让他成为新的master节点的slave节点,复制规则和parallel-syscs参数有关
- 更新对原来master节点配置为slave,并保持对其“关注”,当其恢复后,命令他去复制新的master节点
- 选择“合适的”slave节点
- 选择slave-priority(slave节点优先级)最高的slave节点,如果存在则返回,不存在则继续。
- 选择复制偏移量最大的slave节点(复制的最完整),如果存在则返回,不存在则继续
- 选择runid最新的slave节点
- 从节点的作用
- 副本:高可用的基础
- 扩展:读能力
- 三个消息
- +switch-master:切换主节点(从节点晋升主节点)
- +convert-to-slave:切换从节点(原主节点降为从节点)
- +sdown:主观下线
- 高可用读写分离
- jedisSentinelPool的实现
redis集群
-
redisCluster(3.0开始)
-
呼唤集群
- 为什么呼唤-- 并发量 —>业务需要100万/每秒?
- 机器内存:16-256G —>业务需要500G?
- 配置“强悍”的机器 :超大内存,牛xcpu
- 分布式:简单的认为加机器
- 集群:规模化需求
- 并发量qps
- 数据量:“大数据”
-
数据分布
-
分布式数据库-数据分区
-
顺序分区和哈希分区(例如节点取模)
分布式 特点 典型产品 哈希分布 数据分散度高
键值分布业务无关
无法顺序访问
支持批量操作一致性hashMemercache
redis cluster
其他缓存产品顺序分布 数据分散度倾斜
键值业务相关
可顺序访问
支持批量操作bigTable
HBase- 哈希分布
- 节点取余分区
- hash(key)%nodes 80%迁移
- 翻倍扩容迁移约50%数据
- 客户端分片:hash+取余
- 节点伸缩:数据节点关系变化,导致数据迁移
- 迁移数量和添加的节点数量有关:建议翻倍扩容
- 一致性哈希分区
- 客户端分片:哈希+顺时针(优化取余)
- 节点伸缩:只影响临近的节点,但是还是有数据迁移
- 翻倍伸缩:保证最小迁移数据和负载均衡
- 虚拟槽分区
- 预设虚拟槽:每个槽映射一个数据子集,一般比节点数大(0-16383)
- 良好的哈希函数:例如crc16(key)
- 服务端管理节点,槽,数据:例如redis cluster
- 节点取余分区
- 哈希分布
-
-
搭建集群
- redis cluster架构
- 节点
- cluster-enabled:yes
- meet
- 所有节点共享消息
- 指派槽
- A:0–560
- B:5461-10922
- C:10923-16393
- 复制
- 复制
- 高可用
- 分片
- 节点
- redis cluster架构
-
redis集群的安装
-
两种安装
-
原生命令安装-理解结构
-
配置开启节点
port ${port} daemonize yes dir "/redis/data/" dbfilename "dump-${port}.rdb" logfile "${port}.log" ##集群新增 cluster-enable yes ##记录各个节点的配置 cluster-config-file nodes-${port}.conf
- 使用./redis-server redis-${port}.confg 进行启动
-
meet(实现节点间的通信)
扫描二维码关注公众号,回复: 13129101 查看本文章cluster meet ip port redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7001 ... redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7005
-
cluster节点主要配置
cluster-enabled yes cluster-node-timeout 15000#一般使用默认配置 cluster-config-file "nodes.conf" cluster-require-full-coverage yes#一般配置为no
-
-
指派槽
-
cluster addslots slot[slot …]
redis-cli -h 127.0.01 -p 7000 cluster addslots{0..5461} redis-cli -h 127.0.01 -p 7001 cluster addslots{5462.. 10922} redis-cli -h 127.0.01 -p 7002 cluster addslots{10923..16383}
-
-
主从
-
cluster replicate node-id##和单机的runid不同,这里重启后不会重置
-
redis-cli -h 127.0.0.1 -p 7003 cluster replicate ${node-id-7000}
-
redis-cli -h 127.0.0.1 -p 7004 cluster replicate ${node-id-7001}
-
redis-cli -h 127.0.0.1 -p 7005 cluster replicate ${node-id-7002}
-
sed “s/7000/7001/g” redis-7000.conf > redis-7001.conf(快速生成新的配置文件)
-
查看节点信息:redis-cli -p 7000 cluster nodes
-
查看集群信息:redis-cli -p 7000 cluster info
-
查看槽的分配 redis-cli -p 7000 cluster slots
-
meet操作
- redis-cli -p 7000 cluster meet 127.0.0.1 7001
- redis-cli -p 7000 cluster meet 127.0.0.1 7002
- redis-cli -p 7000 cluster meet 127.0.0.1 7003
- redis-cli -p 7000 cluster meet 127.0.0.1 7004
- redis-cli -p 7000 cluster meet 127.0.0.1 7005
-
指派槽
- redis-cli -h 127.0.01 -p 7000 cluster addslots{0…5461}
redis-cli -h 127.0.01 -p 7001 cluster addslots{5462… 10922}
redis-cli -h 127.0.01 -p 7002 cluster addslots{10923…16383}
- redis-cli -h 127.0.01 -p 7000 cluster addslots{0…5461}
-
主从分配
- redis-cli -p 7003 cluster replicate 7000的节点id
- redis-cli -p 7004 cluster replicate 7001的节点id
- redis-cli -p 7005 cluster replicate 7002的节点id
使用:redis-cli -c -p 7000
-
-
-
官方工具安装
-
ruby环境准备
-
下载,编译,安装ruby
- wget https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz
- tar -xvf ruby-2.3.1.tar.gz
- ./configue -prefix =/usr/local/ruby
- make
- make install
- cd /usr/local/ruby
- cp bin/ruby /usr/local/bin
-
安装rubygem redis
-
wget http://rubygems.org/downloads/redis-3.3.0.gen
-
gem install -l redis-3.3.0.gen
-
gem list – check redis gem
-
-
安装redis-trib.rb
- cp ${REDIS_HOME}/src/redis-trib.rb /usr/local/bin
-
操作步骤
-
首先,进入redis的安装包路径下:
cd /usr/local/src/redis/redis-3.0.1/src/
执行下面创建集群前需要启动对应的redis
-
./redis-trib.rb create --replicas 0 192.168.56.102:6379 192.168.56.102:6380 192.168.56.102:6381
–replicas 0:指定了从数据的数量为0
注意:这里不能使用127.0.0.1,否则在Jedis客户端使用时无法连接到!
可以使用redis-trib.rb check命令检测之前创建的两个集群是否成功,check命令只需要给出集群中任意一个节点地址就可以完成整个集群的检查工作;
-
(error) MOVED 7638 127.0.0.1:6380
因为abc的hash槽信息是在6380上,现在使用redis-cli连接的6379,无法完成set操作,需要客户端跟踪重定向。
redis-cli -c
-
./redis-trib.rb add-node 192.168.56.102:6382 192.168.56.102:6379 新增节点到集群 新增的节点没有slot需要重新分配slot
-
接下来需要给6382这个服务分配插槽,将6379的一部分(1000个)插槽分配给6382:
-
-
-
-
-
总结
- 原生命令安装
- 理解redis cluster 架构
- 生产环境不使用
- 官方工具安装
- 高效,准确
- 其他
- 可视化安装
- 原生命令安装
-
-
-
集群伸缩
- 伸缩原理
- 集群伸缩=槽和数据在节点之间的移动
- 扩容集群
- 准备新的节点
- 集群模式
- 配置和其他节点统一
- 启动后是孤儿节点
- 加入集群
- 127.0.0.1:6379>cluster meet 127.0.0.1 6386
- 127.0.0.1:6379>cluster meet 127.0.0.1 6387
- 作用:为它迁移槽和数据实现扩容,
- 作为从节点负责故障转移
- redis-trib.rb add-node new_host:new_port existing_host:existing_port --slave --master-id
- 建议使用redis-trib.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}命令 ,通知槽分配给目标节点
- 准备新的节点
- 伸缩原理
-
缩容集群
-
迁移前先将槽节点数据进行迁移
./redis-trib.rb reshard --from from _nodeId --to to_nodeId --slots num from_ip:from_port
-
删除节点:
./redis-trib.rb del-node host:port node_id
-
-
-
客户端路由
-
moved重定向
- cluster keyslot php (计算槽的节点)
- redis-cli -c -p 7000
-
ask重定向
-
moved和区别
- 两者都是客户端重定向
- moved:槽已经确定迁移
- ask:槽还在迁移中
-
smart重定向
-
smart客户端原理
- 追求性能
- 从集群中选一个可以运行的节点,使用cluster slots初始化槽和节点映射
- 将cluster slots的结果映射到本地,为每个节点创建jedispool
- 准备执行命令
- key->slot->node
- jedisClustercommand
-
smart客户端使用:jedisCluster
-
jediscluster基本使用
Set<HostAndPort> nodeList=new HashSet<HostAndPort>(); nodeList.add(new HostAndPort(host1,port1)); JedisCluster redisCluster=new JedisClusterClient(nodeList,timeout,poolConfig) redisCluster.command...
-
使用技巧
- 单例:内置了所有节点的连接
- 无需手动借还连接池
- 合理设置commons-pool
-
整合spring
-
多节点命令实现
-
获取到所有节点的JedisPool
-
Map<String,JedisPoolMap> jedisPoolMap=jedisCluster.getClusterNode();
-
for(Entry<String,JedisPoolMap> entry:jedisPoolMap.entrySet()){
//获取每个节点的jedispool连接
Jedis jedis =entry.getValue().getResource();
//只删除主节点的数据
if(!isMaster(jedis)){
continue;
}
}
-
-
批量命令实现
-
批量操作怎么实现 ?
-
mget ,mset 必须在一个槽
-
四种批量优化的方法
- 串行mget
- for循环操作
- 串行io
- 本地计算出对应的hash值和槽得到对应的子集,然后使用pipeline进行操作
- 并行io
- 基于2使用多线程
- hash_tag
- mget(hash_tag)
方案 优点 缺点 网络io 串行mget 编程简单
少量keys满足需求大量keys请求延迟严重 o(keys) 串行io 编程简单
少量节点满足需求大量node延迟严重 o(nodes) 并行io 利用并行特性
延迟取决于最慢的节点编程复杂
超时定位问题难o(max_slow(node)) hash_tag 性能最高 读写增加tag维护成本
tag分布容易出现数据倾斜o(1) - 串行mget
-
-
-
-
-
故障转移
- 故障发现
- 通过ping/pong消息实现故障发现:不需要通过sentinel
- 主观下线和客观下线
- 主观下线:某个节点认为另一个节点不可用,“偏见”
- 客观下线:当半数以上持有槽的主节点都标记某个节点主观下线
- 通知集群内所有节点标记故障节点为客观下线
- 通知故障节点的从节点触发故障转移流程
- 故障发现
-
故障发现
- 主观发现和客观下线
-
故障恢复
-
资格检查
- 每个从节点检查与故障主节点的断线时间
- 超过cluster-node-timeout * cluster-slave-validtiy-factor取消资格
- cluster-slave-validity-factor:默认是10
-
准备选举时间
-
选举投票
-
替换主节点
- 当前从节点取消复制变为主节点(slaveof no one)
- 执行clusterDelSlot撤销故障主节点负责的槽,并执行clusterAddSlot把这些槽分配给自己
-
具体步骤
-
执行kill -9 节点模拟宕机
-
观察客户端故障恢复时间
-
观察各个节点的日志
-
-
-
-
集群原理
-
开发运维常见问题
-
集群完整性
-
cluster-require-full-coverage 默认为yes
-
集群中16384个槽全部可用:保证集群的完整性
-
节点故障或者正在故障转移
-
clusterdown the cluster is down
大多数业务无法容忍:cluster-require-full-coverage 建议设置为no
-
-
-
带宽消耗
- 官方建议节点:1000节点
- ping/pong消息
- 不容忽视的带宽消耗
- 消息发送频率:节点发现和其他节点最后通信时间超过cluster-node-timeout/2时会直接发送ping消息
- 消息数据量:slots槽数组(2k空间)和整个集群1/10的状态数据(10个节点状态数据量约为1kb)
- 节点部署的机器规模:集群分布的机器越多且每台集群划分的节点数越均匀,则集群内整体的可用带宽越高
- 规模:节点200个,20台物理机(每台10个节点)
- cluster-node-timeout=15000,ping/pong带宽为25mb
- cluster-node-timeout=20000,pingt/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
- 优化:优化数据结构
- 内存相关配置不一致
- hash-max-ziplist-value set-max-intset-entries等
- 优化:定期“检查” 配置一致性
- 请求倾斜:热点
- 热点key:重要的key或者bigkey
- 优化:
- 避免使用bigkey
- 热键不要使用hash_tag
- 当一致性不高时,可以使用本地缓存+mq
-
读写分离
- 只读连接:集群模式下的从节点不接受任何读写请求
- 重定向到负责的主节点
- readonly命令可以读:连接级别的命令
- 读写分离:更加复杂
- 同样的问题:复制的延迟,读取过期的数据,从节点故障
- 修改客户端,cluster slaves{nodeId}
- 只读连接:集群模式下的从节点不接受任何读写请求
-
数据迁移
- 离线/在线迁移 官方提供的迁移工具:redis-trib.rb import
- 只能从单机迁移到集群
- 不支持在线迁移:source需要停写
- 不支持断点续传
- 单线程迁移:影响速度
- 在线迁移
- 唯品会 redis-migrate-tool
- 豌豆荚 :redis-port
- 离线/在线迁移 官方提供的迁移工具:redis-trib.rb import
-
集群vs单机
- 集群限制
- key批量操作支持有限:例如mget,mset必须在一个slot
- key事务和lua支持有限:操作的key必须在一个节点
- key是数据分区的最小粒度:不支持bigkey分区
- 不支持多数据库:集群模式只有一个db 0
- 思考-分布式redis不一定好
- rediscluster:满足容量和性能的扩展性,很多业务“不需要”
- 大多数客户端性能会“降低”
- 命令无法跨节点使用:mget,keys,scan,flush,sinter等
- 很多场景redis sentinel已经足够好
- 集群限制
-
-
CLUSTER INFO 打印集群的信息
CLUSTER NODES 列出集群当前已知的所有节点(node),以及这些节点的相关信息。
CLUSTER MEET <ip> <port> 将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。
CLUSTER FORGET <node_id> 从集群中移除 node_id 指定的节点。
CLUSTER REPLICATE <node_id> 将当前节点设置为 node_id 指定的节点的从节点。
CLUSTER SAVECONFIG 将节点的配置文件保存到硬盘里面。
CLUSTER ADDSLOTS <slot> [slot ...] 将一个或多个槽(slot)指派(assign)给当前节点。
CLUSTER DELSLOTS <slot> [slot ...] 移除一个或多个槽对当前节点的指派。
CLUSTER FLUSHSLOTS 移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。
CLUSTER SETSLOT <slot> NODE <node_id> 将槽 slot 指派给 node_id 指定的节点。
CLUSTER SETSLOT <slot> MIGRATING <node_id> 将本节点的槽 slot 迁移到 node_id 指定的节点中。
CLUSTER SETSLOT <slot> IMPORTING <node_id> 从 node_id 指定的节点中导入槽 slot 到本节点。
CLUSTER SETSLOT <slot> STABLE 取消对槽 slot 的导入(import)或者迁移(migrate)。
CLUSTER KEYSLOT <key> 计算键 key 应该被放置在哪个槽上。
CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 目前包含的键值对数量。
CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 个 slot 槽中的键。
CLUSTER SLAVES node-id 返回一个master节点的slaves 列表
redis cluster所有节点IP修改处理方案
1.暂停cluster各个node节点
2.删除持久化文件和节点信息文件:
[root@centos1 6380]# ls
appendonly.aof dump.rdb nodes_6380.conf redis.conf
[root@centos1 6380]# pwd
/usr/local/cluster/6380
[root@centos1 6380]# rm -f appendonly.aof dump.rdb nodes_6380.conf
不删除aof rdb文件会在接下来重新创建节点过程中出现原节点存在数据(Not Empty)异常
3.利用redis-trib.rb 初始化集群信息
dbsize :获取当前数据库大小