关于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
    3
    save 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节点,实现无感知数据迁移。

哨兵模式和分片集群模式区别

  • 哨兵模式是哨兵节点来监控主从节点,若实例下线则开始选举
  • 分片集群模式是主节点直接互相监控,发现某实例下线则开始选举