【精通Redis】Redis持久化和复制


前言

本文主要讨论Redis的持久化方式和复制特性。Redis的持久化方式有两种,一种叫RDB(Redis Database Backup),一种叫只追加文件AOF(append-only-file)。Redis的复制特性主要是用来做Redis集群时,保证各个服务器数据一致性,下面笔者分别展开详细介绍。


一、Redis持久化

1.1 RDB快照

我不知道国内是怎么翻译的,国外的相关技术书籍描述的快照是snapshotting,可能是为了和AOF保证一致的缩写?暂且不管它吧,先说说这种持久化方式的机制。

其实从Redis Database Backup这个名字就能看出来,就是Redis数据库的备份,会在指定时间点创建一个Redis数据库的完整快照,把这个快照存到硬盘。

以下是对 RDB 快照的一些详细说明:

  • 定义:RDB 快照是在某一时间点上数据库的一个完整副本

  • 触发机制:

    • 手动触发:使用 SAVE 命令或 BGSAVE 命令
    • 自动触发:通过配置文件中的 save 指令设置条件,当满足这些条件时,Redis 自动执行快照
  • 优缺点:

    • 优点:恢复速度快,占用较少的磁盘空间
    • 缺点:数据可能存在丢失,因为只保存了某个时间点的数据

手动触发

以下是一个简单代码的实现,演示手动触发RDB快照持久化:

package com.hl.redisdemo;

import redis.clients.jedis.Jedis;

public class RedisRDBSnapshot {
    public static void main(String[] args) {
        // 创建 Jedis 实例
        try (Jedis jedis = new Jedis("127.0.0.1", 6379)) {
            // 设置一些键值对
            jedis.set("key1", "value1");
            jedis.set("key2", "value2");

            // 手动触发 RDB 快照
            String info = jedis.bgsave(); // 触发后台保存 RDB 文件
            System.out.println("RDB 快照已触发: " + info);

            // 查询 RDB 文件保存位置
            String dir = jedis.configGet("dir").get(1);
            String dbfilename = jedis.configGet("dbfilename").get(1);

            System.out.println("RDB 文件保存目录: " + dir);
            System.out.println("RDB 文件名称: " + dbfilename);


            // 获取 RDB 文件的信息
            String rdbInfo = jedis.info("persistence");
            System.out.println("RDB 信息:\n" + rdbInfo);
        } catch (Exception e) {
            System.err.println("执行 RDB 持久化时发生错误: " + e.getMessage());
        }
    }
}


运行上述代码,控制台打印信息如下:

在这里插入图片描述
可以看到已经手动触发生成了一个RDB快照,并存储到磁盘了,目录为 D:\DevloperSofts\redis,名称为dump.rdb,去磁盘该路径下查看可以看到生成了一个持久化的备份文件。
在这里插入图片描述

自动触发

笔者是windows系统安装的Redis,找到配置文件 redis.windows-service.conf

在这里插入图片描述

这几个命令就是设置自动触发RDB的条件

save 900 1
save 300 10
save 60 10000
  • save 900 1
    如果在 900 秒(15 分钟)内至少有一个键被更改,则触发一次 RDB 快照。

  • save 300 10
    如果在 300 秒(5 分钟)内至少有 10 个键被更改,则触发一次 RDB 快照。

  • save 60 10000
    如果在 60 秒(1 分钟)内至少有 10000 个键被更改,则触发一次 RDB 快照。

触发机制

当 Redis 检测到任何满足上述条件之一时,它将自动触发 RDB 快照。如果多个条件都被满足,Redis 只会触发一次快照,而不是多次。

注意事项

  • 如果你设置了多个 save 指令,Redis 将会检查所有条件,一旦任何一个条件满足,就会触发 RDB 快照。

  • 为了防止频繁触发 RDB 快照,可以考虑增加时间间隔或更改键的数量。

  • 确保目标目录存在并且 Redis 服务器有足够的权限写入文件

当然完整的配置我们可以这么写

# RDB 相关配置
dir /var/lib/redis
dbfilename dump.rdb
save 900 1
save 300 10
save 60 10000

# 其他配置
...

  • 使用 dir 指令来指定 RDB 文件的保存目录
  • 使用 dbfilename 指令来指定 RDB 文件的名称

下面我把配置改成如下,在E盘输出备份快照,30秒内只要有一个键被修改,就会自动触发,修改 redis.windows-service.conf
在这里插入图片描述
为什么修改的是 redis.windows-service.conf,而不是 redis.windows.conf?接着往下看

笔者在Windows系统下安装的Redis,并把Redis注册成了系统服务,满足开机自动的要求,从上图可以看到其实有两个配置文件:redis.windows.confredis.windows-service.conf

redis.windows.conf 和 redis.windows-service.conf 是 Redis 在 Windows 平台上使用的两种不同配置文件,它们的主要区别在于服务启动的方式以及某些配置项的差异。

  • redis.windows.conf

    • 这个配置文件主要用于手动启动 Redis 服务器。
    • 当你需要通过命令行手动启动 Redis 服务时,会使用这个配置文件。
    • 通常情况下,如果你不是通过 Windows 服务管理器来启动 Redis,而是直接运行 redis-server.exe redis.windows.conf,那么就会用到这个配置文件
  • redis.windows-service.conf

    • 这个配置文件用于将 Redis 作为一个 Windows 服务来自动启动。
    • 当你希望 Redis 随操作系统启动而自动启动时,会使用这个配置文件。
    • 通常情况下,如果通过 Windows 服务管理器注册 Redis 为一个服务,那么会用到这个配置文件

这两种配置文件在默认情况下可能有相同的设置,但是根据实际需求,你可以对它们进行不同的配置。例如,在作为服务运行时,你可能需要不同的日志记录设置或者资源限制等。

当你安装 Redis 并希望将其设置为 Windows 服务时,通常会使用 redis.windows-service.conf 文件,并且可以通过命令行工具 redis-server.exe --service-install redis.windows-service.conf 来完成服务的安装。同样地,如果你想卸载服务,可以使用 redis-server.exe --service-uninstall 命令。

在这里插入图片描述
通过win+R键,输入 services.msc,找到Redis服务,重启Redis服务,再使用Jedis或者原生Redis命令随便修改一个键值,等待30秒观察结果,笔者的E盘根目录看到如下结果:

在这里插入图片描述
通过上图可以看到,已经自动生成了RDB的快照文件了。

save和bgsave的区别

Redis 中的 SAVE 和 BGSAVE 命令都是用于创建当前数据库的备份,具体来说,它们是在 RDB(Redis Database Backup)快照模式下进行数据持久化的两种方式。下面是它们之间的主要区别:

SAVE

  • SAVE 命令执行一个同步保存操作,将当前 Redis 实例的所有数据快照(snapshot)以 RDB 文件的形式保存到硬盘。

  • 在执行过程中,Redis 主线程会被阻塞,直到保存操作完成。

  • 在阻塞期间,服务器不能处理客户端的任何请求。

  • 由于阻塞主线程,可能会导致性能问题,尤其是在数据量大或硬件较慢的情况下。

BGSAVE

  • BGSAVE 命令执行后立即返回 OK,然后 Redis 会 fork 出一个新子进程。

  • 原来的 Redis 进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。

  • Redis 服务器在 BGSAVE 执行期间仍然可以继续处理客户端的请求。
    使用 BGSAVE 可以避免阻塞主线程,提高系统的可用性。

总结

  • SAVE 命令简单直接,但在数据量较大时可能导致服务不可用。

  • BGSAVE 命令更适用于生产环境,因为它不会阻塞主线程,可以保证服务的连续性。

使用场景

  • 如果需要快速备份数据并且不关心短暂的服务中断,可以选择使用 SAVE。

  • 如果需要在不影响服务的情况下备份数据,则应使用 BGSAVE。

注意事项

  • 在使用 BGSAVE 时,如果 Redis 数据集非常大,fork 操作可能会消耗大量的 CPU 资源,因此在高负载情况下,频繁的 BGSAVE 可能会导致性能下降。

  • 如果 BGSAVE 在执行过程中 Redis 重启或崩溃,可以通过 LASTSAVE 命令查看最后一次成功保存的时间戳,以评估数据丢失的可能性。

1.2 AOF持久化

简单来讲,AOF持久化是把被执行的写命令追加到AOF文件的末尾,以此来记录数据发生的变化。所以Redis只要从头到尾执行一次AOF文件中的所有写命令,就可以恢复AOF文件记录的所有数据集。

下面是AOF持久化的几个关键点:

  1. 命令追加:Redis服务器在执行写操作命令时,会将命令以追加的方式写入到AOF文件的末尾。这样做的好处是可以详细记录下导致数据变更的每一步操作,便于故障恢复时重放这些操作来重建数据状态。

  2. AOF文件的写入策略:通过配置appendfsync参数,用户可以选择AOF日志同步到磁盘的策略。有三种策略可选:

  • always:每次写操作后立即同步到磁盘,最安全,但性能开销最大。
  • everysec:每秒同步一次,折衷方案,性能和安全性相对平衡。
  • no:由操作系统决定何时同步,性能最好,但数据安全性最低。
  1. AOF重写:随着Redis的操作增多,AOF文件可能会变得非常大,这会影响到Redis的重启时间和数据恢复速度。AOF重写机制允许Redis创建一个新的AOF文件,这个新文件只包含重建当前数据集所需的最小命令集合。重写可以通过BGREWRITEAOF命令手动触发,或通过配置自动触发。

  2. AOF文件载入和数据还原:当Redis服务器重启时,如果有AOF文件存在,Redis会优先使用AOF文件来恢复数据,而不是RDB文件。这是因为AOF记录了更完整的历史操作,可以恢复到服务器关闭前的最后状态。

  3. 异常处理:在AOF重写或正常同步过程中如果遇到错误,Redis提供了若干机制来确保数据的完整性和一致性,比如部分重写失败时回滚到之前的AOF文件等。

  4. 使用 AOF 的场景

  • 当数据安全性非常重要时,通常会选择 AOF
  • 如果应用程序能够容忍少量的数据丢失,并且更关心性能,可以选择 RDB 或者同时使用 RDB 和 AOF

AOF日志同步策略中的always策略,这种策略会造成大量的磁盘写入操作,而且是同步的,性能会受到磁盘性能限制。固态硬盘SSD就比轮转式硬盘性能高很多,但是我们的硬盘如果是SSD,SSD每秒可以支持写入几万条命令。若我们使用aways策略时,我们需要注意这种策略会让Redis每次只写入一个命令,命令特别多时,大量的写入会严重降低SSD硬盘的寿命。为了兼顾数据安全和性能通常我们会选择折中的策略,即everysec

appendonly配置

**redis.windows-service.conf**配置文件中修改appendonly为true。

下图所示配置本来是no,改为yes
在这里插入图片描述

然后重启Redis服务即可。笔者提供了一个演示AOF的示例代码

package com.hl.redisdemo;

import redis.clients.jedis.Jedis;

public class AofDemo {
    public static void main(String[] args) {
        try (Jedis jedis = new Jedis("localhost", 6379)) {
            // 设置键值对
            String key = "testKey";
            String value = "你好,AOF!";
            String result = jedis.set(key, value);
            System.out.println("设置结果: " + result);

            // 获取键值对
            String getValue = jedis.get(key);
            System.out.println("获取结果: " + getValue);

            // 检查AOF是否启用
            String aofStatus = jedis.configGet("appendonly").get(1);
            System.out.println("AOF 状态: " + aofStatus);

            // 查询AOF文件的保存路径
            String dirPath = jedis.configGet("dir").get(1);
            System.out.println("AOF 文件保存路径: " + dirPath);

            // AOF文件名默认为 appendonly.aof
            String aofFileName = "appendonly.aof";
            System.out.println("AOF 文件完整路径: " + dirPath + "/" + aofFileName);

            // 如果没有启用,可以尝试通过命令行工具启动AOF
            // 注意:这里只是示例,实际部署中应该在配置文件中设置
            if (!"yes".equals(aofStatus)) {
                System.out.println("正在启用 AOF...");
                // 在生产环境中,这一步应该在部署前完成
                // jedis.configSet("appendonly", "yes");
            }
        }
    }
}


运行结果如下

在这里插入图片描述
也是在E盘,和之前RDB设置的dir路径配置是一样的,两种方式共用一个日志存储路径
在这里插入图片描述

实际开发中应该根据需求决定使用哪种方式,二者各自有适用场景,当然也可以综合使用。

二、Redis复制

在面对高负载时,关系数据库通常会使用一个主服务器向多个从服务器发送数据更新,主数据库负责写,从数据库负责读请求,二者之间使用一些同步机制实现数据的同步。Redis也使用了类似的方法来实现复制特性,作为一种支持性能扩展的手段。

Redis性能尽管十分优秀,但是总会有瓶颈,在对集合Set和有序集合Zset处理时,元素可能有几万甚至上百万个,这时执行操作的时间要以秒来计算了。即使一个命令10毫秒,单个Redis实例1秒也只能执行100个命令。

在我们需要扩展读请求时,或者在需要写入临时数据时,我们可以设置另外的Redis从服务器来保存数据收集的副本。在接收到主服务器发送的数据初始副本时,客户端每次向主服务器进行写入时,从服务器都会实时得到更新。部署好Redis主从服务器后,就可以向任意一个从服务器发送读请求了,不必再像之前一样,读请求都发给主服务器。客户端会随机选择使用哪个从服务器,从而把负载平均分配到各个从服务器上。

2.1 开启Redis主从复制

  1. slaveof host port

    slaveof host port 命令用于配置从服务器连接到主服务器,从而实现数据复制。当你运行这条命令时,从服务器会开始监听主服务器的更新,并将这些更新应用于自己的数据库。

   slaveof <master-ip> <master-port>

master-ip和master-port分别对应主服务器节点的ip地址和端口号

  1. slaveof no one
    slaveof no one 命令用于解除从服务器与主服务器之间的复制关系。当你运行这条命令时,从服务器将不再复制主服务器的数据,并且可以独立地作为主服务器运行。

只需要使用命令在从服务器执行如下

slaveof no one 

或者直接把从服务器配置文件的slaveof配置成如上结果即可。

下面笔者通过命令演示如何把Redis服务设置成从服务器和变成主服务器,实际开发中一般不这么干,都是通过修改配置文件操作的,不能这么随意的修改主从服务器。

  1. 查看初始服务器角色
    在这里插入图片描述
    可以看到初始角色role为master为主节点

  2. 设置为从服务器

执行命令SLAVEOF localhost 6378,设置主服务器节点端口号为6378,再执行INFO replication命令看到已经变成从节点了

在这里插入图片描述

  1. 恢复成主节点
    执行SLAVEOF no one,再次查看结果如下恢复成了主节点

在这里插入图片描述

2.2 Redis复制的启动过程

从服务器在连接一个主服务器时,主服务器会创建一个快照文件并将其发送到从服务器,这只是主从复制执行过程中的一步,下面提供一个表格完整说明主从复制时,主从服务器执行的所有操作。

步骤主服务器操作从服务器操作
1等待命令进入连接主服务器,发送sync同步命令
2开始执行BGSAVE,并使用缓冲区记录BGSAVE之后执行的所有写记录根据配置选项来决定是继续使用现有数据(如果有),还是向发送请求的客户端返回错误
3BGSAVE执行完,向从服务器发送快照文件,并在发送期间继续使用缓冲区记录被执行的写命令丢弃所有旧数据(如果有的话),开始载入主服务器发来的快照
4快照文件发送完毕,开始向从服务器发送存储在缓冲区里的写命令完成快照文件的解释操作后,开始正常接收命令请求
5缓冲区存储的写命令发送完毕后;从现在开始,每接受一个写命令,就向从服务器发送相同的写命令执行主服务器发来的缓冲区中的写命令;从现在开始,接收并执行主服务器发来的每个写命令

实际当中,Redis在复制过程中会尽可能的处理接收到的请求,这就要求我们一定要预留一定的内存,一般主服务器占内存在50%~65%左右,剩下的内存用来执行BGSAVE命令和创建记录写命令的缓冲区。

  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值