目 录CONTENT

文章目录

Redis 的高可用及分布式解决方案

小张的探险日记
2021-09-22 / 0 评论 / 0 点赞 / 657 阅读 / 5,457 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2021-12-29,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

需要集群的原因

  1. 数据安全(可用性保证):在复杂的生成环境下,需要保证redis因为不明原因宕机后服务的可用性,及数据恢复安全。
  2. 高性能:单机的性能在一般的情况下都是足够的,但面对高并发,大流量,一台机器的性能久显得不够看了,需要更多的Redis 服务来提供性能。
  3. 扩展性:单机服务遇到瓶颈后升级硬件的收益不高且成本大,支持水平扩展可以在一定的成本下获得更高的收益🉐️。

主从复制

主从复制可以保证数据的安全,保证在每一台从机下有和主机相同的数据,从而保证数据的安全。

1.主从配置

这里演示一主多从的架构,采用的是6.2.5 版本
配置从机,在从机的redis.conf 中配置:
老版本:slaveof 主机IP:port
新版本:replication 主机IP:port

具体使用那个,查看redis.conf 的配置,有那个使用那个。

本地测试可以使用 3 个配置文件 修改端口的方式来启动 3 个redis 服务

image.png

搭建完成 ✅
从节点只支持读操作,不支持写操作

image.png

通过 info-replication 命令查看当前 主机的 角色。是 slave

image.png

2.主从复制原理

1. 初始化阶段

  1. slave node 启动时会从配置文件读取 主机的 ip 及 端口信息。
  2. slave node 内部会初始化一个 定时任务replicationCron(源码:replication.c),每隔一秒会检查是否有新的 master node 需要连接,通过socket 连接成功后 会安排抓门的事件处理器处理 RDB 文件的负责制,当一个主机变成另一个主机的从机时会 给 master node 发送 ping 请求。

2.数据传播阶段

master node 第一次会执行 bgsave 命令生成 RDB文件,传输给 slave node,slave node 首先会删除 本地数据,然后加载 RDB 数据。

问题
如果数据较多在生成RBD 的同时有命令进来,数据会先写入内存,RDB 生成完成后,会重新传输给 slave node

主从节点的延迟可以通过 repl-disable-tcp-nodelay no 来调整,配置为yes 会对TCP 的包进行压缩,从而减少带宽消耗,但同步频率会减少导致数据一致性差(压缩需要耗时),配置为 no,接收到命令立刻同步给 lasve node 带宽增加但延迟变小

ps: 一般情况下 redis 的应用场景对 数据一致性要求不高,所以默认设置为 NO,当 主从节点网络不好的情况下可以设置为 yes。

master slave node 都有数据的偏移量记录,当slave node 断掉一段时间,重新上线后可以根据偏移量,从指定位置开始同步。

截屏20210922 下午4.41.26.png

主从复制的不足?

  1. RDB体积大 复制慢
  2. 单点可用性问题,master node 宕机后 整个服务不可用,不能手动切master node。

3.高可用 sentinel

sentinel 是特殊的redis 服务,它可以实现对 redis 服务的监控、故障转移、告警通知。但是单个的 sentinel 也有宕机的风险,所以为了实现 高可用 sentinel 也需要集群。

为了保证服务的高可用 sentinel 既监控 redis 也监控 sentinel,sentinel 是互相监控的。

image.png

3.1 redis 服务下线

sentinel 每秒向 各个redis 发送 ping 请求,如果超过阀值没有收到回复,那么sentinel 就会标识redis 服务主观下线,并向其它sentinel 询问,都一致同意后,标识redis 客观下线,这时候会触发选举。

3.2 故障转移

1.第一步,sentinel 集群中需要先选举出来一个leader 来主持这次的故障转移,Sentinel 通过Raft 算法实现Sentinel 选举。

2.Sentinel集群选举出来的 leader 从Redis slave node 节点中选举 master 节点后,不会通知其它的sentinel,其它sentinel 会自动检测,确认master 正常工作后,恢复正常。

slave node 称为 master node 的过程

由Sentinel 的 leader 选举出 master ,发送成为独立节点的命令(slave of no noe),然后向其它 slave node 发送 slave of masterIP:PORT,使其成为 新master 节点的 slave no,故障转移完成 ✅。

那么 master node 的选举标准是什么呢?

  1. 断开连接时长
  2. 进程ID
  3. 数据复制数量
  4. 优先度

超过断开连接时长的阀值直接取消选举资格

优先度 > 复制数据量 > 进程id

3.3 Sentinel 配置搭建

复制出 redis 的redis.conf 配置文件 三份,给到 master 一份,slave 两份,改掉端口和dump.rdb 文件名称

slave 节点需要额外配置 master 节点信息

replicaof 127.0.0.1 6378

master 6379 dump.rdb
slave1 6377 dump6377.rdb
slave2 6378 dump6378.rdb

配置 sentinel 三个 sentinel+端口号.conf
配置内容(注意每个sentinel的端口需要改成不一样,但都是监听master(这里是 127.0.0.1:6379 2,2标识只有两个 sentinel 认为redis 服务下线了,才会真的下线)):

port 26377
daemonize yes
sentinel monitor mymaster 127.0.0.1 6379 2

redis-sentinel-26377.conf
redis-sentinel-26378.conf
redis-sentinel-26379.conf

image.png

启动服务
reids 服务启动:
redis-server master/redis.conf
redis-server slave/redis6377.conf
redis-server slave/redis6378.conf

sentinel 服务启动:
redis-sentinel sentinel/sentinel-6377.conf
redis-sentinel sentinel/sentinel-6378.conf
redis-sentinel sentinel/sentinel-6379.conf

测试:
redis-cli 默认连接到 6379 端口 info replication 命令查看replication 信息

测试 sentinel故障转移

模拟 redis master节点宕机
在 master node 执行 shutdown 命令

可以看到 执行完 shutdown 命令后,重新连接上 6378 redis 服务,角色信息已经变更为 master,如此 sentinel 故障转移就完成了

image.png

redis 服务主从 配合 sentinel 的不足
  1. 只能单点写,不能水平扩容。
  2. 故障转移间隙可能造成服务不可用,丢失数据。

由此 我们更希望 有多个 master 节点 同时提供服务,且都有数据备份,那就是 redis group ,把数据分布到不同的 group 中。

Redis 分布式解决方案

  1. 客户端实现 Sharded
  2. 代理层 如:Twemproxy、Codis
  3. 服务端实现:Redis Cluster
Sharded 案例

两个 redis 服务(独立的),通过 Jedis 的 ShardedJedisPool 写入数据,看到数据 较为均匀的分布到 两个redis 服务

public static void main(String[] args) {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        // Redis 服务器
        JedisShardInfo shardInfo1 = new JedisShardInfo("127.0.0.1", 6378);
        JedisShardInfo shardInfo2 = new JedisShardInfo("127.0.0.1", 6379);
        // 连接池
        List<JedisShardInfo> infoList = Arrays.asList(shardInfo1, shardInfo2);
        ShardedJedisPool jedisPool = new ShardedJedisPool(poolConfig, infoList);
        ShardedJedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            for (int i = 0; i < 100; i++) {
                jedis.set("k" + i, "" + i);
            }
            for (int i = 0; i < 100; i++) {
                System.out.println(jedis.get("k" + i));
            }
        }finally {
            if(null != jedis){
                jedis.close();
            }
        }
    }

代理层
  1. https://github.com/twitter/twemproxy
  2. https://github.com/CodisLabs/codis
Redis 服务端实现

Redis 在3.0后推出了 Redis Cluster来解决分布式需求并实现高可用。

Redis Cluster 搭建三主三从:http://119.3.178.224/archives/mac%E5%8D%95%E6%9C%BA%E5%AE%89%E8%A3%85rediscluster3%E4%B8%BB3%E4%BB%8E

解决数据分布问题,首先会想到哈希取模 对 key做hash操作然后取模redis 服务节点数量 N, hash(key)%N,但 Redis 服务节点出现增加或是减少这种算法就会有问题,因为N 是一个变量,会导致数据需要重新分布。

于是有了一致性哈希 算法,一致性哈希算法是把哈希值控制组成一个圆环,整个空间按顺时针方向计算,初始化时 先按 Redis 节点Ip 做hash,在圆环上定位
,写入的数据先根据key 做hash操作,然后写入圆环上的顺时针找到的第一个节点。

image.png

新增一个节点 node5,那么原本写入node2的数据 就会写入 node5

image.png

删除一个节点node3,原本写入node3的数据都写入 node5

image.png

节点较少时,数据可能分布不均匀

image.png

为了解决 数据分布不均匀的问题,可以引入 虚拟节点,node1-1和node1-2都是node1 的虚拟节点,落到node1-1和node1-2的数据 都会写入 node1

image.png

一致性哈希 数据直接和主机绑定,如果节点增删会影响到附近的节点。

Redis 使用的是 Slot(槽),每个 node 存储 2k=(102428)=16384个槽,每个节点负责一定范围内的槽,在位上标识1则表示当前槽存储在当前服务器,各个节点通过gossip协议通信。

数据插入时对 key 进行CRC16算法计算然后取模16384 落到那个槽则对应的节点负责储存。

通过命令 cluster keyslot haha 可以计算key的槽位。

image.png

Cluster 模式下,对每个key做CRC16 然后取模,映射到不同的节点,但在如事务场景下,操作的数据都要存储到同一个节点否则当执行 trans.Execute()进行事务提交时,直接引发exception,异常日志如下:

"Multi-key operations must involve a single slot; keys can use 'hash tags' to help this, i.e. '{/users/12345}/account' and '{/users/12345}/contacts' will always be in the same slot"。

Redis 提供 Hash Tag 来解决这个问题,如下 ,redis会根据局部节点来做CRC16 取模操作,使相关数据 落到同一个节点。

  • 127.0.0.1:7293> set aa 1 OK
  • 127.0.0.1:7293> set ab 1 OK
  • 127.0.0.1:7293> set ac 1 OK
  • 127.0.0.1:7293> set ad 1 OK
  • 127.0.0.1:7293> set ae 1 OK

客户端重定向

在节点 7291 操作
127.0.0.1:7291>set zpr 123
提示:(error) MOVED 13724 127.0.0.1:7293

表示 计算zpr 的slot 不归 7291管理 而是 7293管理。

一般 客户端实现都会在本地存储 Slot 和 节点的 映射关系

节点新增会涉及到数据迁移

可以指定把原有的 Slot 分给新节点,Redis 会自动完成数据迁移

主从切换

master 节点挂掉后一定时间,slave 会尝试进行故障转移,如果多个slave 会触发竞争,其它的mster 投票,超过半数则 对应的slave 成为新的 master。

优势

  • 无中心架构。
  • 数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。
  • 可扩展性,可线性扩展到 1000 个节点(官方推荐不超过 1000 个),节点可动 态添加或删除。
  • 高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做 standby 数据副 本,能够实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制 完成 Slave 到 Master 的角色提升。
  • 降低运维成本,提高系统的扩展性和可用性。

不足

  • Client 实现复杂,驱动要求实现 Smart Client,缓存 slots mapping 信息并及时 更新,提高了开发难度,客户端的不成熟影响业务的稳定性。
  • 节点会因为某些原因发生阻塞(阻塞时间大于 clutser-node-timeout),被判断 下线,这种 failover 是没有必要的。
  • 数据通过异步复制,不保证数据的强一致性。
0

评论区