Redis 高可用

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis 高可用

Redis 高可用

Redis 主节点宕机后,从节点替换。若该工作由 Redis 来完成,则说明 Redis 是高可用的。

Redis 如何实现高可用

  • 主从复制:分布式一致性解决
  • 主从切换:集群 Cluster,哨兵 Sentinel

高可用的程度由主从切换的速度来决定,如果时间在秒级,则可以说高可用。

1、主从复制

主从复制是数据同步方式,解决了单点故障的问题,不能保证高可用。同时,主从库间采用读写分离的方式,主库可以进行读写操作,当发生写操作自动将写操作同步从库;从库一般是只读,接受主库同步过来的写操作命令,然后执行这条命令。也就是说,所有的数据修改只在主服务器上进行,然后将最新的数据同步给从服务器,这样就使得主从服务器的数据是一致的。

读写分离

1.2、数据同步方式

  • 全量复制:主从第一次同步,主库发送所有数据(生成 RDB 文件和传输 RDB 文件)到从库
  • 基于长连接的命令传播:第一次同步完成后,主从维护着一个长连接,主库收到写操作命令后,通过这个连接将写命令传播给从库,保证主从库的数据一致性
  • 增量复制:从库断开重连,主库补发丢失数据到从库

redis 主从复制

1.2、主从复制的实现

runid

用于验证主从之间的关系,每个 Redis 节点启动时都生成 runid。

从库对主库初次复制时,主库将自己的 runid 传递给从库,从库将其保存。当从库断线重连主库时,从库将向主库发送之前保存的 runid

  • 若主从库的 runid 一致,说明从库断线前复制的就是当前主库,主库尝试执行增量同步操作
  • 若主从库的 runid 不同,说明从库断线前复制的不是当前主库,主库对从库执行全量同步操作

环形缓冲区

避免从库断线重连主库后开始全量同步。

环形缓冲区(复制积压缓冲区 ,repl_backlog_buffer)本质上是一个固定长度队列,可以避免数据移动。当缓冲满了,覆盖起始位置的旧数据。该缓冲区会在从库失联期间累计主库的写操作。

当从库重连,发送自己的复制偏移量到主库,主库会比较主从的复制偏移量

  • 若从库偏移量还在缓冲区内,进行增量同步
  • 若从库偏移量不在缓冲区内,进行全量同步

复制偏移量

主从库都维护各自的复制偏移量 offset,

  • 主库向从库发送 n 字节的数据:offset = offset + n
  • 从库接收主库发送 n 字节的数据:offset = offset + n

通过比较主从复制偏移量得知主从数据是否一致,偏移量相同则数据一致,否则数据不一致。

1.3、* 主从数据不一致

主从复制采用异步复制:Redis 主节点每次收到写命令后,异步发送给从节点

这样会出现主从数据不一致,具体来说

  • 长连接命令传播阶段,主节点收到新的写命令,发送给从节点,主节点并不等待从节点实际执行完写命令后,再把结果返回给客户端,而是主节点在本地执行完命令后,向客户端返回结果。此时,若从节点还未执行完主节点同步过来的命令,则主从节点间数据短暂不一致。
  • 主库宕机后,从库丢失主库一部分执行的命令,主从数据不一致。

若要求一致性,则规定只能读取主库而不是从库。

2、哨兵模式

2、哨兵模式

Sentinel 是 Redis 可用性的解决方案。Sentinel 通过 ping-pong 心跳检测的方法监控主从服务器,并在主服务器下线时自动对其实施故障转移,从而实现高可用。

客户端连接集群时,会首先连接 Sentinel,通过 Sentinel 来查询主节点的地址,然后再连接主节点。当主节点发生故障时,客户端会重新向 Sentinel 索要主节点地址,Sentinel l将最新的主节点地址告诉客户端。通过这样客户端无须重启即可自动完成节点切换。

Sentinel 节点个数是奇数,不存储数据,用来监控节点的状态和选举主节点,只提供一个数据节点服务。Sentinel 节点不仅监控 Redis 主从节点,同时还互相监控,形成多哨兵模式。

Sentinel 模式当中涉及的多个选举流程采用的是 raft 一致性算法。

sentinel 模式

2.1、Sentinel 职责

哨兵的职责有:

  • 第一轮投票:判断主节点下线:主观下线 + 客观下线
  • 第二轮投票:哨兵选主
  • 由哨兵 leader 进行主从故障转移:从库选主 + 故障转移

判断节点故障

  • 主观下线:适用于所有 Redis 节点 (主从节点 + Sentinel 节点)。Sentinel 以每秒 1 次的频率向所有 Redis 节点发送 ping 消息,然后通过接收目标节点的 pong 回复,来判断该节点是否下线。如果在规定时间内没有收到回复,则判定该节点主观下线。
  • 客观下线:仅适用于主节点,当一个 Sentinel 节点判定一个主节点主观下线后,会向其他 Sentinel 节点询问对该主节点状态的判断。如果 Sentinel 节点超半数判定该主节点已下线,该判定该主节点客观下线。

针对主节点设计主观下线和客观下线两个状态,是因为主节点可能并没有故障,可能只是因为主节点系统压力大或者网络发生拥塞,导致主节点没有在规定时间内响应。因此,为了减少误判,需要多个哨兵节点组成集群共同判断主节点状态,防止误判主节点下线。

哨兵选主

  • 哨兵选主 leader:所有 Sentinel 节点通过投票机制,按照谁发现谁处理的原则,选举 Sentinel Leader 节点,这需要 Sentinel 节点半数以上支持。

主从故障转移

由哨兵 leader 进行主从故障转移

  • 从库选主: Sentinel leader 节点按照一定的规则在所有从节点中选择一个作为新的主节点。
  • 故障转移:通知其他节点复制连接新的主节点,并将新主节点的信息发布订阅给客户端。若故障主节点重新上线,则作为新的主节点的从节点。

2.2、Sentinel 集群

哨兵以集群的方式部署,哨兵节点通过 Redis 的发布订阅机制,互相感知,互相连接,组成哨兵集群。同时,哨兵通过 INFO 命令,在主节点获取所有从节点的连接信息,和从节点建立连接,并进行监控。

2.3、Sentinel 缺点

  • 部署时间长
  • 没有解决数据丢失问题,异步复制
  • 没有数据扩展机制

主从切换数据丢失

主从切换过程中,产生数据丢失的情况有两种

  • 异步复制同步丢失:主从节点同步数据异步复制,若主节点还没来得及同步给从节点时发生宕机,主节点数据丢失。
  • 集群产生脑裂数据丢失:主节点宕机后,主从数据不同步,重新选举产生新的主节点。旧主节点回来后降级为从节点,与新主节点进行同步时,从节点清空自己的缓冲区,丢失数据。

我们不能保证数据完全不丢失,只能做到尽量少的数据丢失。

3、Redis 集群

3.1、Cluster 原理

3.1.1、复制与高可用

Redis 集群与单机 Redis 一样,提供了主从复制功能。在 Redis 集群中,各个 Redis 服务器称为 Redis 节点,主节点负责处理客户端发送的读写命令,从节点负责对主节点进行复制。这是因为主从复制是异步复制,会造成主从节点不一致,因此使用主节点。同时,Redis 集群各个节点相互监视各自的运行状态,并在主节点下线时,通过提升该主节点的从节点为新主节点来继续提供服务。

3.1.2、分片与重分片

Redis 集群通过将数据库分散存储到多个节点上来平衡各个节点的负载压力,实现了 Redis 的分布式存储。要想做到主节点间数据的负载均衡,需要使用分布式一致性 hash。

分布式一致性 hash 算法crc (key) % 16384 。Redis 集群将整个数据库空间划分为 16384 (214) 个哈希槽 slot 来实现数据分片,每个主节点负责处理其中的一部分槽。

这样做的好处是,固定算法,当集群扩容或缩容时,不会因为节点的增删,而导致路由失效。当增加节点时,集群就会将相应的槽以及槽中存储的数据迁移至新节点;当删除节点时,被移除的节点将自己负责的槽以及槽中数据转交给集群中的其他结点。更重要的是,整个分片 reshard 过程都可以在线进行,redis 集群不会因此停机。

3.2、Cluster 特征

  • 去中心化,主节点关系对等。
  • 解决了数据扩容
  • 客户端与服务端缓存槽位信息,以服务端为准,客户端缓存主要为了避免连接切换
  • 可人为数据迁移
  • 主节点处理读写命令。

3.3、Cluster 流程

  • 连接集群中任意一个节点
  • 若数据不在该节点,回收到连接切换的命令,继而连接到目标节点
  • 故障转移(主节点下移)
  • 集群结点间互相监控,交换节点的状态信息
  • 若某主节点下线,将会被其他主节点标记下线
  • 接着从下线主节点的从节点中选择一个数据最新的从节点作为主节点
  • 从节点继承下线主节点的槽位信息,并广播该消息给集群中的其他节点

缺点:主从异步复制在故障转移时仍存在数据丢失的问题

3.4、Cluster 管理

redis 管理集群提供两种方法:集群管理工具 redis-cli 和 redis 内置的集群管理命令

下面案例将要使用到的 redis-cli 命令如下:

redis-cli --cluster help
 # 创建集群
 create <ip>:<port>
 --cluster-replicas <num>    # 创建集群的同时,为每个主节点配备的从节点个数
 # 查看集群的信息,群中任意节点地址作为参数,后面同理
 info <ip>:<port>    
 # 检查集群的配置
 check <ip>:<port>   
 # 重分片,将指定数量的槽从源节点迁移至目标节点,由目标节点负责迁移的槽和槽中数据
 reshared <ip>:<port>
 --cluster-from  # 源节点的ID
 --cluster-to    # 目标节点的ID
 --cluster-slots <num> # 需要迁移的槽数量
 # 添加节点,添加新节点 new 集群 existing,默认添加主节点
 add-node <new_host>:<port> <existing_host>:<port>
 # 添加从节点,需要以下两个子命令
 --cluster-slave 
 --cluster-master-id <id> # 设置从节点要复制的主节点
 # 移除节点
 del-node <ip>:<port> <id>

3.4.1、搭建集群

设置启动配置文件

# 创建文件夹
 mkdir -p 7001 7002 7003 7004 7005 7006
 cd 7001
 vi 7001.conf
 # 输入内容
 pidfile "/home/jtz/redis-data/7001/7001.pid"   
 logfile "/home/jtz/redis-data/7001/7001.log"    
 dir /home/mark/redis-data/7001/
 port 7001       
 daemonize yes    
 cluster-enabled yes  
 cluster-config-file nodes-7001.conf
 cluster-node-timeout 15000
 # 复制配置
 cp 7001/7001.conf 7002/7002.conf
 cp 7001/7001.conf 7003/7003.conf
 cp 7001/7001.conf 7004/7004.conf
 cp 7001/7001.conf 7005/7005.conf
 cp 7001/7001.conf 7006/7006.conf
 # 修改配置
 sed -i 's/7001/7002/g' 7002/7002.conf
 sed -i 's/7001/7003/g' 7003/7003.conf
 sed -i 's/7001/7004/g' 7004/7004.conf
 sed -i 's/7001/7005/g' 7005/7005.conf
 sed -i 's/7001/7006/g' 7006/7006.conf
 # 创建启动脚本
 vi start.sh
 # 输入内容
 #!/bin/bash
 redis-server 7001/7001.conf
 redis-server 7002/7002.conf
 redis-server 7003/7003.conf
 redis-server 7004/7004.conf
 redis-server 7005/7005.conf
 redis-server 7006/7006.conf
 # 创建启动脚本
 vi stop.sh
 redis-cli -p 7001 shutdown
 redis-cli -p 7002 shutdown
 redis-cli -p 7003 shutdown
 redis-cli -p 7004 shutdown
 redis-cli -p 7005 shutdown
 redis-cli -p 7006 shutdown

创建集群

# 创建集群
 redis-cli --cluster create 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 127.0.0.1:7006 --cluster-replicas 1

搭建成功,查看集群配置信息

redis-cli --cluster check 127.0.0.1:7001
 # 集群配置信息
 M: 6d67700c3a40ea80488a48c232f9459e664bf899 127.0.0.1:7001
    slots:[0-5460] (5461 slots) master 
    1 additional replica(s)
 S: 9a66a88e4b4da0edeb2767d55dbd4623f0f2ec52 127.0.0.1:7004
    slots: (0 slots) slave
    replicates 8b8339a497d07237b1fce6abef45d251132fc594
 S: 3171812cc5b5e956c142321d6d31e68fa8708872 127.0.0.1:7006
    slots: (0 slots) slave
    replicates e4da2884848793a8deb973fe623097d5089a8031
 S: be0058062baca91b2a27f884c6cebd59f24f0cd2 127.0.0.1:7005
    slots: (0 slots) slave
    replicates 6d67700c3a40ea80488a48c232f9459e664bf899
 M: 8b8339a497d07237b1fce6abef45d251132fc594 127.0.0.1:7003
    slots:[10923-16383] (5461 slots) master
    1 additional replica(s)
 M: e4da2884848793a8deb973fe623097d5089a8031 127.0.0.1:7002
    slots:[5461-10922] (5462 slots) master
    1 additional replica(s)

测试集群

redis-cli -c -p 7001
 127.0.0.1:7001> set name mark
 -> Redirected to slot [5798] located at 127.0.0.1:7002 # 分配到其他主节点存储
 OK
 redis-cli -c -p 7003
 127.0.0.1:7003> get name
 -> Redirected to slot [5798] located at 127.0.0.1:7002 # 重定向到存储该key的节点
 "mark"
 127.0.0.1:7002>

3.4.2、主从切换

# 主节点宕机,发生主从切换
 redis-cli -p 7003 shutdown
 # 主节点重启
 redis-server 7003/7003.conf
 # 配置信息变化
 # 初始状态
 M: 8b8339a497d07237b1fce6abef45d251132fc594 127.0.0.1:7003
    slots:[10923-16383] (5461 slots) master
    1 additional replica(s)
 # 主从切换后,从节点7004替换宕机的主节点7003
 M: 9a66a88e4b4da0edeb2767d55dbd4623f0f2ec52 127.0.0.1:7004
    slots:[10923-16383] (5461 slots) master
    1 additional replica(s)
    replicates 6d67700c3a40ea80488a48c232f9459e664bf899
 # 节点7003上线后,成为其他节点的从节点   
 S: 8b8339a497d07237b1fce6abef45d251132fc594 127.0.0.1:7003
    slots: (0 slots) slave

3.4.3、主从扩容

先添加节点,再分配槽位

# 1、先添加节点
 # 添加主节点,新添加的节点默认作为主节点
 redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7001 
 # 添加从节点
 redis-cli --cluster add-node 127.0.0.1:7008 127.0.0.1:7001  --cluster-slave --cluster-master-id 6d67700c3a40ea80488a48c232f9459e664bf899
 # 2、再分配槽
 # 重分片,将主节点 7001 的 1000 个槽迁移至新的主节点 7007
 redis-cli --cluster reshard 127.0.0.1:7001 --cluster-from 6d67700c3a40ea80488a48c232f9459e664bf899 --cluster-to 5043e35b99e3c3e45241dd7b748502aa8daf544c --cluster-slots 1000
 # 对比迁移前后的集群配置
 # 迁移前
 M: 6d67700c3a40ea80488a48c232f9459e664bf899 127.0.0.1:7001
    slots:[0-5460] (5461 slots) master
    2 additional replica(s)
 M: 5043e35b99e3c3e45241dd7b748502aa8daf544c 127.0.0.1:7007
    slots: (0 slots) master
 # 迁移后
 M: 6d67700c3a40ea80488a48c232f9459e664bf899 127.0.0.1:7001
    slots:[1000-5460] (4461 slots) master
    1 additional replica(s)
 M: 5043e35b99e3c3e45241dd7b748502aa8daf544c 127.0.0.1:7007
    slots:[0-999] (1000 slots) master
    1 additional replica(s)

3.4.4、主从缩容

先移动槽位,再删除节点

# 1、先移动槽位
 # 将主节点 7007 的所有槽迁移至主节点 7001
 redis-cli --cluster reshard 127.0.0.1:7001 --cluster-from 5043e35b99e3c3e45241dd7b748502aa8daf544c --cluster-to 6d67700c3a40ea80488a48c232f9459e664bf899 --cluster-slots 1000
 # 迁移后的集群配置
 M: 6d67700c3a40ea80488a48c232f9459e664bf899 127.0.0.1:7001
    slots:[0-5460] (5461 slots) master
    2 additional replica(s)
 M: 5043e35b99e3c3e45241dd7b748502aa8daf544c 127.0.0.1:7007
    slots: (0 slots) master
 # 2、删除节点
 # 删除节点 7007
 redis-cli --cluster del-node 127.0.0.1:7001 5043e35b99e3c3e45241dd7b748502aa8daf544c
 # 此时7008成为其他节点的从节点
 M: 6d67700c3a40ea80488a48c232f9459e664bf899 127.0.0.1:7001
    slots:[0-5460] (5461 slots) master
    2 additional replica(s)
 S: 1d5f5827cdb9f294de702f4247d1471fce9545df 127.0.0.1:7008
    slots: (0 slots) slave
    replicates 6d67700c3a40ea80488a48c232f9459e664bf89
 # 删除从节点7008
 redis-cli --cluster del-node 127.0.0.1:7001 1d5f5827cdb9f294de702f4247d1471fce9545df
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
7月前
|
存储 监控 NoSQL
Redis 高可用之主从模式
上一节RDB和AOF持久化机制提到了 Redis 的持久性,也就是在服务器实例宕机或故障时,拥有再恢复的能力。但是在这个服务器实例宕机恢复期间,是无法接受新的数据请求。对于整体服务而言这是无法容忍的,因此我们可以使用多个服务器实例,在一个实例宕机中断时,另外的服务器实例可以继续对外提供服务,从而不中断业务。Redis 是如何做的呢?Redis 做法是**增加冗余副本**,**将一份数据同时保存在多个实例**上。那么如何保存各个实例之间的数据一致性呢?
80 0
Redis 高可用之主从模式
|
7月前
|
NoSQL 关系型数据库 MySQL
Redis高可用之主从复制架构(第一部分)
Redis高可用之主从复制架构(第一部分)
|
7月前
|
机器学习/深度学习 NoSQL Redis
Redis高可用之集群架构(第三部分)
Redis高可用之集群架构(第三部分)
|
7月前
|
消息中间件 NoSQL Redis
Redis高可用之哨兵模式(第二部分)
Redis高可用之哨兵模式(第二部分)
|
4月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
116 2
基于Redis的高可用分布式锁——RedLock
|
5月前
|
监控 NoSQL Redis
Redis 哨兵模式高可用
Redis 哨兵模式高可用
86 4
|
2月前
|
存储 NoSQL 大数据
大数据-51 Redis 高可用方案CAP-AP 主从复制 一主一从 全量和增量同步 哨兵模式 docker-compose测试
大数据-51 Redis 高可用方案CAP-AP 主从复制 一主一从 全量和增量同步 哨兵模式 docker-compose测试
35 3
|
5月前
|
负载均衡 NoSQL 应用服务中间件
搭建高可用及负载均衡的Redis
【7月更文挑战第10天】
150 1
|
6月前
|
存储 运维 NoSQL
Redis 分区:构建高性能、高可用的大规模数据存储解决方案
Redis 分区:构建高性能、高可用的大规模数据存储解决方案
|
7月前
|
存储 监控 NoSQL
Redis是如何保证高可用的?
通过这些机制,Redis可以在主节点故障或其他异常情况下保持高可用性,确保数据的可靠性和可用性。不过,为了实现高可用性,需要仔细规划和配置Redis集群,并确保监控和故障恢复机制的可靠性。
199 6