Redis 高可用
一、Redis 高可用概述
在Web服务器中,高可用性主要指系统正常运行的时间,如99.9%、99.99%、99.999%等,但在Redis语境中,高可用的含义更加宽泛。除了确保服务的可用性,Redis的高可用还涉及数据容量扩展和数据安全性等方面。为了实现Redis的高可用,主要采用以下几项技术:持久化、主从复制、哨兵、以及Cluster集群。下面分别介绍这些技术的作用及解决的问题。
1.1 持久化
持久化是最基本的高可用手段,虽然有时它甚至不被认为是高可用的直接手段,但它却是保证数据安全的重要环节。
作用:
- 数据备份:持久化可以将内存中的数据存储到硬盘中,确保即使Redis服务器进程意外退出或重启,数据也不会丢失。
解决的问题:
- 数据安全:Redis进程因崩溃或重启时,数据可以从持久化文件(RDB或AOF)中恢复,防止数据丢失。
虽然持久化可以保证数据的安全性,但它不能独立解决Redis高可用性问题,因为它无法应对服务器故障、网络问题等导致的服务中断。
1.2 主从复制
主从复制是Redis实现高可用性的基础,所有的高可用架构,如哨兵和集群,都是建立在主从复制的基础上。
作用:
- 多机数据备份:主节点的数据会复制到从节点,确保当主节点故障时,数据依然可以在从节点上找到。
- 读操作负载均衡:从节点可以分担主节点的读请求,减轻主节点的压力。
- 简单故障恢复:通过手动切换,当主节点故障时,可以将从节点提升为新的主节点,恢复服务。
解决的问题:
- 数据备份:数据被复制到多台机器上,增强了数据的可靠性。
- 读性能提升:通过读写分离,主节点负责写操作,从节点负责读操作,提升了系统的读性能。
缺陷:
- 自动化不足:主从复制的故障恢复过程需要人工干预,无法实现自动化。
- 写操作的瓶颈:写操作只能由主节点处理,无法分散到其他节点。
- 存储能力受限:所有数据依然存储在主节点上,受单机内存大小的限制。
1.3 哨兵(Sentinel)
哨兵在主从复制的基础上,进一步增强了Redis的高可用性,解决了主从复制的自动化问题。哨兵系统负责监控Redis集群的运行状态,并在出现故障时自动进行故障转移。
作用:
- 自动故障检测与转移:哨兵系统持续监控主从节点的健康状态,当主节点发生故障时,它能够自动将一个从节点提升为新的主节点,完成故障转移,确保系统的正常运行。
- 通知系统管理员:哨兵系统还可以通知管理员节点的状态变化,便于及时采取措施。
解决的问题:
- 故障自动恢复:通过哨兵的自动化故障检测和主从切换,减少了人为干预的时间,极大地提升了系统的高可用性。
缺陷:
- 写操作无法负载均衡:写操作依然只能由主节点处理,导致写性能依然受到限制。
- 存储能力仍受单机限制:虽然故障恢复机制更加完善,但存储能力依然依赖于主节点的内存大小。
1.4 Cluster集群
Cluster集群是Redis 3.0后引入的分布式方案,它解决了哨兵模式下无法负载均衡写操作以及存储受单机限制的问题。
作用:
- 数据分区:通过将数据分散到多个节点(即数据分片),Redis Cluster大大提高了集群的存储容量。每个主节点负责一部分数据,从而突破了单机内存限制,提升了整体存储能力。
- 读写负载均衡:每个主节点不仅可以处理读请求,还可以处理写请求,这解决了主从模式下写操作的单点瓶颈问题。
- 高可用和自动故障恢复:Cluster集群通过主从复制和自动化的主从切换机制,在节点发生故障时可以自动恢复,确保服务的高可用性。
解决的问题:
- 写操作负载均衡:通过将数据分片并分布在多个主节点上,实现了写操作的负载均衡,避免单个节点成为写操作的瓶颈。
- 存储扩展:数据分片打破了单机内存的限制,Redis可以根据需要水平扩展节点,提升存储容量。
- 高可用:通过自动故障恢复机制,当任意主节点出现问题时,其从节点能够自动接管,保证集群服务的持续性。
总结:
Cluster集群是Redis高可用的最终方案,它解决了持久化、主从复制和哨兵模式中存在的存储受限、写操作瓶颈等问题,提供了全面的高可用性和可扩展性。
1.5 Redis高可用技术总结
- 持久化:主要解决数据安全问题,防止数据丢失,但无法应对服务中断问题。
- 主从复制:实现数据多机备份、读写分离,提升读性能,但缺乏自动化故障恢复,且写操作无法分散。
- 哨兵模式:在主从复制的基础上,增强了自动故障检测与恢复的能力,但依然存在写操作瓶颈和存储限制。
- Cluster集群:解决了写操作负载均衡和存储受限问题,实现了全面的高可用方案,适合需要大规模数据存储和高性能读写的场景。
二、Redis持久化
Redis是一种内存数据库,数据存储在内存中。为了防止由于服务器断电等意外导致的数据丢失,Redis提供了持久化机制,将数据保存到硬盘上。当Redis重启时,可以利用这些持久化文件恢复数据。此外,持久化文件还可以用于灾难备份,通过将文件拷贝到远程位置来提高数据的安全性。
Redis提供了两种持久化方式:RDB和AOF。下面将详细介绍这两种持久化机制及其工作原理。
2.1 RDB持久化
RDB(Redis DataBase)持久化是通过定期将Redis内存中的数据快照保存到硬盘来实现的。这种持久化方式也称作快照持久化,生成的文件后缀为.rdb
。当Redis重启时,可以读取这个快照文件恢复数据。
2.1.1RDB持久化的触发
-
手动触发:
SAVE
命令:会阻塞Redis服务器,直到RDB文件创建完成,期间服务器无法处理其他命令。BGSAVE
命令:会创建一个子进程来生成RDB文件,父进程继续处理请求。BGSAVE
是推荐的方式,因为它不会阻塞服务器。
-
自动触发:
- 通过配置文件中的
save
选项来设置自动触发条件。以下示例配置表示:save 900 1
:每900秒内发生至少1次修改时,触发BGSAVE
。save 300 10
:每300秒内发生至少10次修改时,触发BGSAVE
。save 60 10000
:每60秒内发生至少10000次修改时,触发BGSAVE
。
- 在主从复制场景下,主节点执行全量复制时会触发
BGSAVE
。 - 执行
SHUTDOWN
命令时也会自动触发RDB持久化。
- 通过配置文件中的
2.1.2执行流程
- Redis父进程检查是否已有正在执行的
SAVE
或BGSAVE
子进程,如果有,则直接返回。 - 父进程执行
FORK
操作创建子进程,这个过程中父进程会被阻塞,不能处理其他命令。 BGSAVE
命令返回“Background saving started”,父进程继续处理其他请求。- 子进程创建RDB文件,生成临时快照文件,完成后进行原子替换。
- 子进程通知父进程完成,父进程更新统计信息。
2.2 AOF持久化
AOF(Append Only File)持久化记录Redis执行的每条写命令,日志文件会以追加的方式写入。Redis重启时通过执行AOF文件中的命令来恢复数据。由于AOF的实时性更好,它已经成为主流的持久化方式。
开启AOF
要启用AOF持久化,需要在配置文件中设置:
appendonly yes #700行--修改,开启AOF
appendfilename "appendonly.aof" #704行--指定AOF文件名称
aof-load-truncated yes #796行--是否忽略最后一条可能存在问题的指令
2.2.1执行流程
- 命令追加:将Redis的写命令追加到缓冲区
aof_buf
,而不是直接写入文件,以提高写入效率。 - 文件写入和同步:
appendfsync always
:每次写命令都立即同步到硬盘,性能较差,适用于对数据安全要求极高的场景。appendfsync no
:不进行同步,依赖操作系统,数据安全性较低。appendfsync everysecond
:每秒同步一次,性能和安全性的折中方案,是推荐的配置。
- 文件重写:定期重写AOF文件,减少文件体积,提高恢复速度。文件重写将内存数据转换为写命令,写入新的AOF文件,不会对旧文件进行操作。
2.2.2文件重写的触发
- 手动触发:通过执行
BGREWRITEAOF
命令,类似于BGSAVE
,会创建子进程进行操作。 - 自动触发:通过设置
auto-aof-rewrite-min-size
和auto-aof-rewrite-percentage
来自动执行文件重写。例如:auto-aof-rewrite-percentage 100
:当AOF文件大小是上次重写时的两倍时,触发重写。auto-aof-rewrite-min-size 64mb
:AOF文件达到64MB时触发重写。
2.2.3文件重写的流程
- Redis父进程检查是否有正在执行的
BGSAVE
或BGREWRITEAOF
,若有则直接返回。 - 父进程执行
FORK
操作创建子进程,过程中父进程阻塞。 BGREWRITEAOF
命令返回“Background append only file rewrite started”,父进程继续处理请求。- 子进程生成新的AOF文件,处理过程中使用
aof_rewrite_buf
缓存写命令,保证新AOF文件的数据完整性。 - 子进程完成后,父进程将
aof_rewrite_buf
中的数据写入新文件,替换旧文件,完成重写。
2.3 RDB与AOF持久化的优缺点
RDB持久化(快照)
-
优点:
- 文件紧凑: RDB文件体积小,便于存储和传输。
- 恢复速度快: 由于RDB文件是全量备份,恢复速度比AOF快。
- 对性能影响小: RDB对Redis的性能影响较小,适合需要较高性能的场景。
-
缺点:
- 实时性差: RDB的快照方式无法做到实时持久化,可能导致数据丢失,尤其在数据频繁变化时。
- 兼容性问题: RDB文件需要特定格式,不同版本的Redis可能不兼容。
- IO压力: 在bgsave执行时,Redis主进程会阻塞,子进程写数据也会带来IO压力。
AOF持久化(追加日志)
-
优点:
- 秒级持久化: AOF支持秒级持久化,能更好地保障数据安全。
- 兼容性好: AOF文件格式相对简单,兼容性较好。
-
缺点:
- 文件大: AOF文件通常比RDB大,存储和传输成本较高。
- 恢复速度慢: 由于AOF文件记录了所有写操作,恢复速度较慢。
- 对性能影响大: AOF写数据频率高(如每秒),会增加IO压力,可能导致主进程性能下降。AOF文件的重写也会带来类似的IO压力问题。
2.4 Redis启动时的数据恢复机制
-
AOF优先加载: Redis启动时会优先加载AOF文件来恢复数据。
-
AOF文件不存在时: 如果AOF文件不存在,Redis会加载RDB文件(如果存在)来恢复数据。
-
AOF文件校验:
- 如果AOF文件损坏,Redis启动会失败,并在日志中记录错误。
- 如果AOF文件尾部不完整(如因宕机),且
aof-load-truncated
参数开启(默认开启),Redis会忽略尾部不完整的部分,并在日志中输出警告,成功启动。
三、主从复制
Redis主从复制是将一台Redis服务器的数据复制到其他Redis服务器的过程。复制是单向的,即从主节点(Master)到从节点(Slave)。这种机制为Redis提供了数据冗余、故障恢复、负载均衡和高可用性的基础。
3.1 主从复制的作用
-
数据冗余:
- 主从复制实现了数据的热备份,为持久化数据提供了一种额外的冗余方式。
-
故障恢复:
- 当主节点发生故障时,从节点可以接管服务,提供快速的故障恢复。
-
负载均衡:
- 主从复制结合读写分离的策略,可以由主节点处理写操作,而由从节点处理读操作。在写少读多的场景下,通过多个从节点分担读操作负载,大幅提高服务器的并发处理能力。
-
高可用基石:
- 主从复制是Redis高可用架构(如哨兵和集群)的基础,确保系统的可靠性和稳定性。
3.2 主从复制流程
-
从节点请求同步:
从节点启动后向主节点发送SYNC
命令,发起同步请求。 -
主节点保存快照:
主节点启动后台进程,保存当前数据快照(RDB 文件),并记录后续的修改操作。 -
数据同步到从节点:
主节点将快照文件发送给从节点,从节点将数据文件保存并加载到内存。随后,主节点继续将数据修改操作同步给从节点。 -
多从节点处理:
若有多个从节点,主节点会并发发送数据文件,确保所有从节点数据一致。
3.3 搭建Redis主从复制
Master节点: 192.168.10.23
Slave1节点: 192.168.10.14
Slave2节点: 192.168.10.15
systemctl stop firewalld
systemctl enable firewalld
setenforce 0
-----安装 Redis-----
yum install -y gcc gcc-c++ make
tar zxvf redis-5.0.7.tar.gz -C /opt/
wget -p /opt http://download.redis.io/releases/redis-5.0.9.tar.gz
cd /opt/redis-5.0.7/
make
make PREFIX=/usr/local/redis install
cd /opt/redis-5.0.7/utils
./install_server.sh
......
Please select the redis executable path [/usr/local/bin/redis-server] /usr/local/redis/bin/redis-server
ln -s /usr/local/redis/bin/* /usr/local/bin/
-----修改 Redis 配置文件(Master节点操作)-----
vim /etc/redis/6379.conf redis.conf
bind 0.0.0.0 #70行,修改监听地址为0.0.0.0
daemonize yes #137行,开启守护进程
logfile /var/log/redis_6379.log #172行,指定日志文件目录
dir /var/lib/redis/6379 #264行,指定工作目录
appendonly yes #700行,开启AOF持久化功能
/etc/init.d/redis_6379 restart
-----修改 Redis 配置文件(Slave节点操作)-----
vim /etc/redis/6379.conf
bind 0.0.0.0 #70行,修改监听地址为0.0.0.0
daemonize yes #137行,开启守护进程
logfile /var/log/redis_6379.log #172行,指定日志文件目录
dir /var/lib/redis/6379 #264行,指定工作目录
replicaof 192.168.10.23 6379 #288行,指定要同步的Master节点IP和端口
appendonly yes #700行,开启AOF持久化功能
/etc/init.d/redis_6379 restart
-----验证主从效果-----
在Master节点上看日志:
tail -f /var/log/redis_6379.log
Replica 192.168.10.14:6379 asks for synchronization
Replica 192.168.10.15:6379 asks for synchronization
在Master节点上验证从节点:
redis-cli info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.10.14,port=6379,state=online,offset=1246,lag=0
slave1:ip=192.168.10.15,port=6379,state=online,offset=1246,lag=1
四、哨兵模式
在Redis的主从复制中,当主节点宕机时,必须手动将从节点切换为新的主节点,这不仅麻烦而且会导致服务中断。为了解决这个问题,Redis引入了哨兵(Sentinel)机制,自动处理主节点的故障转移,确保系统的高可用性。
4.1 哨兵模式原理
- 哨兵(Sentinel): 是一种分布式系统,监控主节点和从节点的状态。当主节点出现故障时,哨兵会通过投票机制选举新的主节点,并让所有从节点连接到这个新的主节点。哨兵集群通常由至少3个节点组成,以保证可靠性。
4.2 哨兵模式的作用
- 监控: 哨兵不断检查主节点和从节点的状态,确保它们正常运行。
- 自动故障转移: 当主节点出现故障时,哨兵会自动将某个从节点升级为新的主节点,并让其他从节点复制新的主节点的数据。
- 通知(提醒): 哨兵可以将故障转移的结果通知客户端,保证系统信息的及时更新。
4.3 哨兵结构
- 哨兵节点: 哨兵节点是特殊的Redis实例,不存储数据,仅用于监控和管理主从节点。
- 数据节点: 主节点和从节点是数据节点,存储实际的数据。
4.4 Redis 故障转移机
-
检测主节点故障:
- 哨兵节点定期发送心跳检测(ping)到主节点。如果主节点在指定时间内没有响应或回复错误,哨兵会认为它发生了故障。
-
确认故障:
- 如果大多数哨兵节点都认为主节点故障,主节点将被标记为“客观下线”。
-
选举领导哨兵:
- 哨兵节点通过选举算法(如Raft)选出一个领导哨兵,负责处理故障转移。
-
执行故障转移:
- 领导哨兵将一个从节点升级为新的主节点。
- 其他从节点将连接到新的主节点,并复制其数据。
- 如果原主节点恢复,将其转为从节点,并连接到新的主节点。
- 通知客户端主节点已更换。
4.5 搭建Redis 哨兵模式
Redis哨兵模式需要在主从复制的基础上
Master节点:192.168.10.23
Slave1节点:192.168.10.14
Slave2节点:192.168.10.15
-----修改 Redis 哨兵模式的配置文件(所有节点操作)-----
vim /opt/redis-5.0.7/sentinel.conf
protected-mode no #17行,关闭保护模式
port 26379 #21行,Redis哨兵默认的监听端口
daemonize yes #26行,指定sentinel为后台启动
logfile "/var/log/sentinel.log" #36行,指定日志存放路径
dir "/var/lib/redis/6379" #65行,指定数据库存放路径
sentinel monitor mymaster 192.168.10.23 6379 2 #84行,修改 指定该哨兵节点监控192.168.10.23:6379这个主节点,该主节点的名称是mymaster,最后的2的含义与主节点的故障判定有关:至少需要2个哨兵节点同意,才能判定主节点故障并进行故障转移
sentinel down-after-milliseconds mymaster 30000 #113行,判定服务器down掉的时间周期,默认30000毫秒(30秒)
sentinel failover-timeout mymaster 180000 #146行,故障节点的最大超时时间为180000(180秒)
-----启动哨兵模式-----
先启master,再启slave
cd /opt/redis-5.0.7/
redis-sentinel sentinel.conf &
-----查看哨兵信息-----
redis-cli -p 26379 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.20.23:6379,slaves=2,sentinels=3
-----故障模拟-----
#查看redis-server进程号:
ps -ef | grep redis
root 57031 1 0 15:20 ? 00:00:07 /usr/local/bin/redis-server 0.0.0.0:6379
root 57742 1 1 16:05 ? 00:00:07 redis-sentinel *:26379 [sentinel]
root 57883 57462 0 16:17 pts/1 00:00:00 grep --color=auto redis
#杀死 Master 节点上redis-server的进程号
kill -9 57031 #Master节点上redis-server的进程号
#验证结果
1. tail -f /var/log/sentinel.log
2.redis-cli -p 26379 INFO Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.10.14:6379,slaves=2,sentinels=3
四、Redis 集群模式
Redis集群(Redis Cluster)是Redis 3.0引入的一种分布式存储解决方案,它由多个节点组成,并通过数据分区和主从复制实现高性能和高可用性。
4.1 Redis 集群的两大作用
-
数据分区:
- Redis集群通过数据分片将数据分散到不同的节点上,打破了单机内存的限制,提升存储容量。
- 每个主节点可以独立处理读写请求,增加集群的响应速度和并发处理能力。
-
高可用:
- Redis集群支持主从复制和主节点的自动故障转移。当主节点发生故障时,从节点可以接管服务,确保集群继续运行。
4.2 Redis 集群的数据分片机制
Redis集群通过哈希槽来管理数据。每个集群有 16384个哈希槽(编号0-16383)。集群中的每个节点负责管理一部分哈希槽,数据通过以下流程分片:
- 每个键会经过CRC16算法进行哈希计算。
- 计算结果对16384取余,得到哈希槽编号。
- 根据这个哈希槽编号,找到对应的节点进行数据存储或读取。
示例: 在一个由三个节点组成的集群中:
- 节点A负责槽0到5460,
- 节点B负责槽5461到10922,
- 节点C负责槽10923到16383。
4.3 Redis 集群的主从复制模型
- 集群的主节点负责数据处理,每个主节点有一个从节点来备份数据。
- 假设集群中有A、B、C三个主节点及其从节点A1、B1、C1。如果主节点B发生故障,从节点B1将被选举为新的主节点继续提供服务。
- 但如果主节点B和从节点B1同时故障,整个集群将不可用。
4.4 搭建Redis 集群模式
redis的集群一般需要6个节点,3主3从。方便起见,这里所有节点在同一台服务器上模拟:
以端口号进行区分:3个主节点端口号:6001/6002/6003,对应的从节点端口号:6004/6005/6006。
cd /etc/redis/
mkdir -p redis-cluster/redis600{1..6}
for i in {1..6}
do
cp /opt/redis-5.0.7/redis.conf /etc/redis/redis-cluster/redis600$i
cp /opt/redis-5.0.7/src/redis-cli /opt/redis-5.0.7/src/redis-server /etc/redis/redis-cluster/redis600$i
done
#开启群集功能:
#其他5个文件夹的配置文件以此类推修改,注意6个端口都要不一样。
cd /etc/redis/redis-cluster/redis6001
vim redis.conf
#bind 127.0.0.1 #69行,注释掉bind 项,默认监听所有网卡
protected-mode no #88行,修改,关闭保护模式
port 6001 #92行,修改,redis监听端口,
daemonize yes #136行,开启守护进程,以独立进程启动
cluster-enabled yes #832行,取消注释,开启群集功能
cluster-config-file nodes-6001.conf #840行,取消注释,群集名称文件设置
cluster-node-timeout 15000 #846行,取消注释群集超时时间设置
appendonly yes #700行,修改,开启AOF持久化
#启动redis节点
分别进入那六个文件夹,执行命令:redis-server redis.conf ,来启动redis节点
cd /etc/redis/redis-cluster/redis6001
redis-server redis.conf
for d in {1..6}
do
cd /etc/redis/redis-cluster/redis600$d
redis-server redis.conf
done
ps -ef | grep redis
#启动集群
redis-cli --cluster create 127.0.0.1:6001 127.0.0.1:6002 127.0.0.1:6003 127.0.0.1:6004 127.0.0.1:6005 127.0.0.1:6006 --cluster-replicas 1
#六个实例分为三组,每组一主一从,前面的做主节点,后面的做从节点。下面交互的时候 需要输入 yes 才可以创建。
--replicas 1 表示每个主节点有1个从节点。
#测试群集
redis-cli -p 6001 -c #加-c参数,节点之间就可以互相跳转
127.0.0.1:6001> cluster slots #查看节点的哈希槽编号范围
1) 1) (integer) 5461
2) (integer) 10922 #哈希槽编号范围
3) 1) "127.0.0.1"
2) (integer) 6003 #主节点IP和端口号
3) "fdca661922216dd69a63a7c9d3c4540cd6baef44"
4) 1) "127.0.0.1"
2) (integer) 6004 #从节点IP和端口号
3) "a2c0c32aff0f38980accd2b63d6d952812e44740"
2) 1) (integer) 0
2) (integer) 5460
3) 1) "127.0.0.1"
2) (integer) 6001
3) "0e5873747a2e26bdc935bc76c2bafb19d0a54b11"
4) 1) "127.0.0.1"
2) (integer) 6006
3) "8842ef5584a85005e135fd0ee59e5a0d67b0cf8e"
3) 1) (integer) 10923
2) (integer) 16383
3) 1) "127.0.0.1"
2) (integer) 6002
3) "816ddaa3d1469540b2ffbcaaf9aa867646846b30"
4) 1) "127.0.0.1"
2) (integer) 6005
3) "f847077bfe6722466e96178ae8cbb09dc8b4d5eb"
127.0.0.1:6001> set name zhangsan
-> Redirected to slot [5798] located at 127.0.0.1:6003
OK
127.0.0.1:6001> cluster keyslot name #查看name键的槽编号
redis-cli -p 6004 -c
127.0.0.1:6004> keys * #对应的slave节点也有这条数据,但是别的节点没有
1) "name"