欢迎光临
我们一直在努力

【Redis】持久化策略

一.RDB

1.1策略

1.2手动进行快照命令

  • save VS bgsave

    • save :使用主进行进行持久化指令,save时只管保存,其它不管,全部阻塞。手动保存。不建议。

    • bgsave:Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。 可以通过lastsave 命令获取最后一次成功执行快照的时间

  • flushall命令

    • 执行flushall命令,也会产生dump.rdb文件,但里面是空的,无意义

  • shutdown命令

    • shutdown命令在关闭服务的时候也会进行自动的持久化

1.3流程

1. 接收 BGSAVE 命令

客户端执行 BGSAVE,请求 Redis 生成 RDB 快照文件。

2. 检查互斥条件

父进程先检查:

  • 是否有其他子进程正在执行持久化(如正在执行 BGSAVE 或 BGREWRITEAOF);
  • 如果有,直接返回错误,避免同时执行导致资源竞争或数据混乱。
    为什么只能fork一个子进程

   fork() 操作本身会消耗大量的 CPU 和内存资源(尤其是在内存很大的实例上)。如果同时 fork 出两个子进程,系统资源会被瞬间耗尽,导致主进程(Redis 服务)被长时间阻塞,无法处理任何请求。

        子进程生成后,虽然共享父进程的内存,但一旦父进程有新的写入操作,就会触发内存页的复制。

  • 如果只有一个子进程,复制操作是可控的。
  • 如果有两个子进程同时存在,父进程的每一次写入都需要复制两份内存页给两个子进程,内存带宽和 CPU 负载会成倍增加,导致服务性能急剧下降。

3. fork 创建子进程

父进程通过 fork() 系统调用创建一个子进程:

  • 写时复制(Copy-On-Write):子进程共享父进程的内存空间,只有当父进程修改数据时,才会复制对应的内存页;
  • 这样既保证了子进程能拿到一份完整的内存快照,又避免了瞬间占用双倍内存的问题。

4. 父进程继续响应命令

fork 完成后,父进程可以继续处理其他客户端的读写请求,不会被阻塞,这就是 “后台保存” 的含义。(直接写在自己的内存中,而不是缓冲区!子进程拿到是“当前的快照”)

5. 子进程生成 RDB 文件

子进程遍历 Redis 的所有数据库,将内存中的键值对按 RDB 格式序列化到磁盘文件(默认 dump.rdb):

  • 子进程只负责写入,不处理新的写入请求,保证 RDB 文件的一致性;
  • 生成完成后,原子地替换旧的 RDB 文件,避免文件损坏。

6. 子进程通知父进程完成

子进程完成 RDB 文件生成后,退出并通知父进程:

  • 父进程更新内部状态,记录最后一次成功保存的时间;
  • 如果子进程异常退出,父进程会记录错误日志。

1.4缺点

        *  一次要遍历全部数据,开销很大,并且要考虑到几乎2倍的内存膨胀。

        *  定时快照可能会丢失数据。

流程是这样的:

  • 00:00 → 拍了一次 RDB(数据保存成功)
  • 00:05 → 你写入了 100 条数据
  • 00:10 → 你又写入 200 条数据
  • 00:15 → 服务器突然宕机!
  • 结果:00:00~00:15 这 15 秒内的 300 条数据全部丢失!

    1.5优点

             在down掉之后,可以很快恢复。

    二.AOF

             为了解决RDB方案,开销需要遍历全部数据,内存需要膨胀很大的问题,我们引入只记录修改操作的AOF方案。

    2.1策略

    (1)客户端的请求写命令会被append追加到AOF缓冲区内(如果每次都直接记录进.aof文件中,频繁的IO操作会导致性能被拖垮,所以我们可以先记录到AOF缓冲区中,定期进行写入.aof文件中);

    (2)AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;

    (3)AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;

    这个重写是,看能不能以更少的命令完成等价的更小的命令来替代它。

    (4)启动子进程写REWRITE的时候,如果主进程还继续在写入数据。那么可能会出现读写不一致的情况。所以我们先写入AOF_REWRITE_BUFFER中。

    之后先存入之前的修改操作到aof文件中,再从缓冲区读入随后的修改操作。

    2.2手动生成.aof文件

    操作核心动作对文件的影响目的类比场景
    aof_fsync(落盘) 追加缓冲区内容到旧文件 体积增加,内容不变 防止缓冲区数据丢失 草稿纸立刻抄进日记本
    BGREWRITEAOF(重写) 生成精简新文件替换旧文件 体积大幅减小 给 AOF 文件瘦身 重新整理日记本,删掉无用内容

    2.3流程

    2.4缺点

            备份数据没RDB快,占用更多的磁盘空间

    2.5优点

    • 备份机制更稳健,丢失数据概率更低。

    • 可读的日志文本,通过操作AOF稳健,可以处理误操作。

    我的理解

            AOF,RDB在fork出子进程的时候,其实都是当前的快照给子进程进行复制。只是RDB给的当前的所有记录的快照,而AOF给的是修改操作的快照。然后在子进程同步数据的时候,RDB复制快照,如果主进程有修改,就直接写入内存,等下次的时间到再重新同步心写入的内容。而AOF是从aof_buffer缓冲区读入,子进程同步期间,如果主进程还在写入修改操作,就放在aof_rewrite_buffer中,等子进程同步完成了之后,再从缓冲区中取数据

    三.主从Redis的数据同步机制

    3.1作用

            实现读写分离提高效率和高可用性(redis是单线程,客户连接不到Redis了,会等待redis重启,可用性不高)。

    3.2实现思路

    • 1 一个redis服务作为主机,主要负责写操作

    • 2 两个redis服务作为从机,主要负责读操作

    • 3 从机自动从主机同步数据下来

    • 4 从机主动找主机,而主机不会找从机

    • 5 正常来说主机和从机应该在不同的IP上开启redis服务,我们为了快速模拟,可以在一台机器上模拟出三个redis服务即可

    3.3数据同步原理

    • Slave启动成功连接到master后会发送一个sync命令

    • Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步

    • 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。

    • 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步

    • 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行

    3.4问题 1:slave1、slave2 是从头开始复制还是从切入点开始复制?比如从 k4 进来,那之前的 k1,k2,k3 是否也可以复制?

    核心结论

    • 首次连接:必须从头全量复制(k1、k2、k3、k4 都会复制),没有 “只从 k4 开始” 的说法;
    • 断连重连:如果断连时间短,从「断连的切入点(比如 k4)」增量复制;如果断连太久,会重新全量复制。

    3.5问题 2:从机是否可以写?set 可否?

    核心结论

    • 默认禁止写:从节点执行set key value会直接报错(error) READONLY You can't write against a read-only replica.;
    • 手动开启写(绝对不推荐):修改配置replica-read-only no(Redis5.0+,旧版是slave-read-only no)能写,但会导致数据不一致,开发 / 生产中严禁这么做。

    3.6问题 3:(不用哨兵时)主机 shutdown 后情况如何?从机是上位还是?

    核心结论

    • 默认不会自动上位:主节点 shutdown 后,所有从节点会变成 “失联状态”,依然是从节点身份,只读,不会主动变成主节点;
    • 手动 / 哨兵上位:需要人工执行SLAVEOF NO ONE让某个从节点变主节点,或用哨兵(Sentinel)自动切换。

    原理拆解

  • 主节点宕机后,从节点会不断尝试重连主节点,失败后就处于 “等待主节点恢复” 的状态,不会主动切换角色;
  • 手动切换步骤(面试必说):

    redis

    # 在选中的从节点执行,断开和旧主节点的连接,变成新主节点
    SLAVEOF NO ONE
    # 其他从节点重新指向新主节点
    SLAVEOF 新主节点IP 端口

  • 生产环境用哨兵(Sentinel):哨兵监控主节点状态,宕机后自动选同步最完整的从节点升级为主节点,无需人工操作。
  • 3.7问题 4:主机又回来了后,主机新增记录,从机还能否顺利复制?

    核心结论

    • 默认不能:旧主节点重启后,依然是 “主节点身份”,不会主动变成从节点,它新增的记录不会同步给原从节点;
    • 手动处理后可以:需要把旧主节点设为 “新主节点的从节点”,才能重新加入集群同步数据。

    原理拆解

  • 场景还原:主节点宕机→从节点 A 被设为新主节点→旧主节点重启(此时集群有两个主节点:旧主 + 新主);
  • 旧主节点新增的记录,原从节点(除了 A)依然是新主节点的从节点,不会同步旧主的记录;
  • 正确操作(面试必说):

    redis

    # 在旧主节点执行,让它变成新主节点(A)的从节点
    SLAVEOF 新主节点IP 端口

    执行后,旧主节点会先全量同步新主节点的最新数据(包括自己宕机期间新增的内容),之后新写入的记录也会同步给所有从节点。

  • 3.8问题 5:其中一台从机 down 后情况如何?依照原有它能跟上大部队吗?

    核心结论

    • 短时间 down 机:重启后能 “跟上大部队”,从断连的切入点增量复制缺失的记录;
    • 长时间 down 机:重启后会触发全量同步(重新复制所有数据),但依然能跟上,只是耗时更长;

    四.Redis的Sentinel机制

    4.0为什么引入哨兵机制

            Redis 的主从复制模式下,⼀旦主节点由于故障不能提供服务,需要⼈⼯进⾏主从切换,同时⼤量的客⼾端需要被通知切换到新的主节点上,对于上了⼀定规模的应⽤来说,这种⽅案是⽆法接受的, 于是Redis从2.8开始提供了RedisSentinel(哨兵)加个来解决这个问题。        

    4.1哨兵数量

            只有一个哨兵,存在单点问题,可能会因为网络而产生误判。且哨兵挂了,就监控不了主从节点的状态了。

    一般是一个节点(无论主从)带一个哨兵,并且确保是奇数个,因为要少数服从多数。

    4.2哨兵的作用

    Redis 哨兵(Sentinel)的 4 大核心作用

    1. 监控(Monitoring)

    • 不停地检查 主节点、从节点 是否正常运行。
    • 发现主节点挂了,立刻开始处理。

    2. 自动故障转移(Automatic Failover)⭐【最重要】

    • 主节点挂掉后,自动从多个从节点里选一个最合适的变成新主节点。
    • 其他从节点自动去同步新主节点。
    • 不用人工干预,这就是高可用。

    3. 通知(Notification)

    • 节点出问题时,哨兵可以通过 脚本、邮件 等方式通知管理员。

    4. 配置提供者(Configuration Provider)

    • 客户端连接 Redis 时,不直接连主节点,而是先连哨兵。
    • 哨兵告诉客户端:当前谁是主节点。
    • 主节点切换后,客户端自动拿到新地址。

    4.3选择主节点的原则

    1.断开主节点的时间最短

    2.优先级越高(硬件属性好的)

    3.读取增量文件(command_list.txt)读取便宜了最多的

    4.4从节点挂了怎么办

    一句话:啥也不用干,等它重启就行,完全不影响主节点和业务。

    重启后自动:

  • 连回主节点
  • 判断断连时间:
    • 短时间断开 → 增量同步(只补缺失的数据)
    • 长时间断开 → 全量同步(重新 RDB 同步)
  • 追上主节点最新数据
  • 继续正常提供读服务
  • 它自己就能追上大部队,不用人工操作。

    五.Redis集群

    5.0为什么引入Redis集群

    (1)写并发:

            Redis单实例读写分离可以解决读操作的负载均衡,但对于写操作,仍然是全部落在了master节点上面,在海量数据高并发场景,一个节点写数据容易出现瓶颈,造成master节点的压力上升。

    (2)海量数据的存储压力:

            单实例Redis本质上只有一台Master作为存储,如果面对海量数据的存储,一台Redis的服务器就应付不过来了,而且数据量太大意味着持久化成本高,严重时可能会阻塞服务器,造成服务请求成功率下降,降低服务的稳定性。

    针对以上的问题,Redis集群提供了较为完善的方案,解决了存储能力受到单机限制,写操作无法负载均衡的问题。

    5.1数据分片

    5.1.1怎么分发到服务器上

    key / mod = AimRedis

    5.1.2固定长度为16384(2^14),这样不会产生随redis集群数量增多,导致模数变化

    为什么取16384

    2^14 = 16384

    • 是 2 的整数次幂(2¹⁴),取模运算效率极高(计算机算 2 的幂取模只需位运算);
    • 数值大小适中,既能均匀分配数据,又不会让分片数量过多(管理成本高)。

    5.1.3Redis哨兵集群怎么互相知道key是属于谁

    他们会互相维护,各个Redis集群存储的Hash桶范围

    5.1.4key分配举例

    由「1 主 + 2 从 + 3 哨兵」组成

    取模结果范围是 0~16383,我们把这个范围划分给 4 个分片集群:

    • 分片集群 1:负责 0~4095(16384/4=4096)
    • 分片集群 2:负责 4096~8191
    • 分片集群 3:负责 8192~12287
    • 分片集群 4:负责 12288~16383
    订单 ID计算过程(订单 ID % 16384)取模结果归属分片集群
    1234 1234 ÷ 16384 = 0 余 1234 1234 集群 1
    5678 5678 ÷ 16384 = 0 余 5678 5678 集群 2
    98765 98765 ÷ 16384 = 6 余 56785%16384=98765 – 6×16384=98765-98304=461 461 集群 1

    5.2怎么新增Redis集群

    我们不会改变固定长度:16384长度的哈希槽数组。

    我们只需要知道新的Redis集群将负责哪儿部分的哈希槽,然后迁移这部分的数据就好啦。(而不会像以前全部迁移数据了。)

    赞(0)
    未经允许不得转载:171主机测评 » 【Redis】持久化策略
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址