关于Redis的分布式缓存问题
关于Redis的持久化
RDB持久化
RDB(Redis DataBase Backup file),也叫Redis数据快照。主要工作方法是在一定时间间隔内将Redis在某一时刻的数据保存到磁盘中的RDB文件中。
主要有两条命令触发RDB:
- save:手动触发RDB持久化。由于Redis是单线程,所以使用save命令时会造成所有请求的阻塞,直到save命令执行完后才可以执行其他请求。
- bgsave:后台异步执行RDB持久化,不会阻塞主进程。推荐
自动触发RDB
在 Redis 配置文件(通常是 redis.conf)中,可以通过save < seconds > < changes >指令配置自动触发 RDB 持久化的条件。这个指令可以设置多次,每个设置定义了一个时间间隔(秒)和该时间内发生的变更次数阈值。1
2
3save 900 1 (有1个键被修改,900秒后自动触发一次RDF)
save 300 10 (有10个键被修改,300秒后自动触发一次RDF)
save 60 10000 (有10000个键被修改,60秒后自动触发一次RDF)关于“bgsave”的流程(fork)
在运行bgsave命令时会通过fork()系统调用创建子进程,子进程共享主进程的内存数据。读取内存数据并写入RDB文件。
主进程不直接操作物理内存,而是将内存快照写入虚拟内存中。操作系统会维护一张虚拟内存和物理内存的映射关系表,叫做页表。主进程基于页表的映射关系对物理内存进行读写。
执行fork()时,主进程把页表拷贝给子进程,这样我们就无需拷贝内存数据但是可以操作物理内存,效率很高。问题
当我们的主进程在写入数据而同时子进程在读出数据时,会发生老生常谈的安全问题,可能会读到脏数据。
而伟大的Redis早已想到了这一切。
fork采用的是copy-on-write机制,即当主进程执行读操作时,主进程和子进程可以访问共享内存一起读。在主进程执行写操作时,共享内存会被设置成只读模式,同时复制一份拷贝的副本用来执行主进程的写操作。从此以后主进程的页表映射关系就转换为了复制的副本。
注意 因为cow机制给主进程准备了一份副本随便修改,而子进程读取的仍然是原来的共享内存,所以主进程和子进程在修改后的数据是不一致的。子进程是一次性的进程,子进程在完成了RDB的写入后会通知父进程,父进程使用新的RDB文件替换原来的文件。优点
RDB生成快照对Redis性能影响小
RDB文件紧凑,适合传输、备份缺点
RDB执行间隔长,两次RDB间写入数据有丢失风险
fork子进程、压缩、写出RDB文件都较为耗时
AOF持久化
AOF是通过记录每个写操作的命令并将其追加到AOF文件中,恢复时重新执行这些命令来重建数据,以保证持久化。
AOF持久化需要配置是否开启,默认关闭。1
appendonly yes
AOF提供了三种方案以保证持久化,分别是:
- always:每执行一条命令都同步追加到AOF文件,保证数据完整性。(可靠性高,但是性能较差)
- everysec:每秒钟同步一次AOF文件,保证数据完整性。推荐(性能适中,最多损失1s的数据)
- no:关闭AOF持久化。(性能高,但是可靠性为0)
BGREWRITEAOF机制
由于AOF持久化的应对方案是把所有数据操作命令都写入AOF文件中,这就会导致如果我们set num 123,之后我们又set num 456 ,这两个命令都会记录在AOF文件中,这就造成了AOF文件过大,并且恢复时需要执行大量的命令。
为了解决这个问题,Redis提供了BGREWRITEAOF命令,它会创建一个新的AOF文件,重写原有的AOF文件,用最少的命令达到相同的效果。
但就算AOF使用了该命令,AOF文件还是会比RDB生成的文件要大,因为AOF记录的是所有命令,但是RDB记录的只是数据。优点
灵活,实时性强,每次写入命令就同步。在频繁写入条件下,AOF比RDB更可靠。缺点
AOF文件过大,性能影响大,恢复速度慢。
Redis的数据恢复流程
Redis在进行数据恢复时会优先判断AOF是否开启且文件是否存在,若存在则优先使用AOF来进行数据恢复。若AOF关闭或AOF文件不存在Redis才会使用RDB进行数据恢复。
关于Redis的主从机制
主从复制 读写分离
Redis的主从复制是实现Redis高可用和数据分片的一种方式。
主从复制的原理是:一个主Redis服务器负责处理所有的写操作,并将数据同步到从Redis服务器。从Redis服务器可以进行读操作,但数据来自主Redis服务器。这样就保证了Redis的读写分离,保证高可用。
主从复制数据同步原理(全量同步)
第一次主从复制是全量同步:
- 从服务器请求数据同步,携带Replid和偏移量
- 主服务器通过replid判断从服务器是不是第一次同步
- 是第一次,主服务器返回master的replid和offset
从服务器保存版本信息
主服务器执行bgsave命令生成RDB文件,同时记录RDB期间的所有命令到repl_backlog文件中
- 主服务器将RDB文件发送给从服务器
- 从服务器接收RDB文件并写入到磁盘
从服务器清空旧数据,加载RDB文件
主服务器发送repl_backlog文件中的命令
- 从服务器执行命令,将数据同步到从服务器
master判断slave是否是第一次同步
- Replication id:master的唯一标识,slave会在第一次同步时继承masters的Replication id,并在之后的同步中保持相同的Replication id。当master和slave的Replication id相同时,就可以判断不是第一次同步,所以不使用全量同步。
- Offset:master的当前偏移量,slave会在第一次同步时继承masters的偏移量,并在之后的同步中保持相同的偏移量。若slave的偏移量小于master的偏移量,则说明master的数据有更新,slave需要进行同步。
- 第一次同步时,master会记录当前的Replication id和偏移量,并发送给slave。
增量同步
- 从服务器请求数据同步,携带replid和偏移量
- 主服务器根据replid判断是否第一次同步
不是第一次,返回continue
主服务器从repl_backlog中读取偏移量之后的数据,并返回给从服务器
- 从服务器执行命令,将数据同步到从服务器
注意
repl_backlog文件有上限,当大小超过上限就会覆盖之前的数据。如果slave断开的太久,导致尚未备份的数据被覆盖,则slave只能再次进行全量同步。
集群优化
- 在master中配置repl-diskless-sync yes,启动无磁盘复制,减少磁盘IO。此方法适用于磁盘读写能力差但网络带宽高的场景。
- Redis单节点内存占用不要过大,减少RDB导致的过多IO。
- 适当提高repl_backlog文件大小,发现slave宕机尽快恢复,尽量避免全量同步。
- 限制master上的slave数量,slave数量太多情况下可以采用主-从-链式结构,减轻master的压力。
哨兵机制(Sentinel)(解决高可用问题)
用于监控Redis的主从复制,自动完成故障转移并通知管理员。
监控
Sentinel会不断检查master和slave是否按期工作。
服务状态监控
Sentinel基于心跳机制监测服务状态,每隔1秒向集群中的每个实例发送ping命令。
- 主观下线:某Sentinel节点发现某实例未在一定时间内响应,则认为该实例主观下线。
- 客观下线:若超过指定数量的Sentinel节点都认为某实例主观下线,则认为该实例客观下线。指定数量需超过Sentinel节点数的一半。
自动故障恢复
当master出现故障时,Sentinel会自动选举一个slave作为新的master。故障恢复后也会以心跳的master为主。选举依据
- 判断slave节点与master节点之间的断开时间长短,时间超出指定值(down-after-milliseconds * 10)的slave节点会被排除。
- 判断slave节点的slave-priority值,越小优先级越高,但是0不会被选举。
- 若slave-priority相同,则选择偏移量较大的slave,偏移量越大代表数据越新。
- 判断slave节点的运行id大小,越小优先级越高。
通知
Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息通知给客户端。故障转移
- Sentinel给备选的slave节点发送 slaveof no one命令,让该节点成为master。
- Sentinel给所有其他slave节点发送 slaveof ip port命令,让它们成为新master的从节点。
- Sentinel将故障节点标记为slave,故障节点恢复后会自动成为新master节点的从节点。
分片集群 (cluster)(解决高可用和分布式)
集群将数据分布到多个Redis节点上,通过分片的方式来实现高可用和分布式。存储容量大大增加,同时大大提高集群的响应能力。
实例映射
集群中数据和实例的映射是通过哈希槽(Hash Slot)来实现的。Redis集群有16384个哈希槽,每个节点均匀负责一部分哈希槽。
判断key在哪个实例
根据key的有效部分计算哈希值,对16384取余,余数作为插槽,寻找插槽所在实例
将同一类数据固定保存在同一个Redis实例
- 这一类数据使用相同的有效部分,例如key都以{typeId}为前缀
故障转移
- master宕机情况
1.该实例与其他实例断开连接
2.实例疑似宕机
3.确定下线,自动提升一个slave当master手动故障迁移(不知道有什么用)
利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的slave节点,实现无感知数据迁移。
哨兵模式和分片集群模式区别
- 哨兵模式是哨兵节点来监控主从节点,若实例下线则开始选举
- 分片集群模式是主节点直接互相监控,发现某实例下线则开始选举






