【精通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快照持久化:

java 复制代码
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.conf

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

java 复制代码
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 快照。

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

触发机制

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

注意事项

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

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

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

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

java 复制代码
# 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.conf redis.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的示例代码

java 复制代码
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 命令用于配置从服务器连接到主服务器,从而实现数据复制。当你运行这条命令时,从服务器会开始监听主服务器的更新,并将这些更新应用于自己的数据库。

lua 复制代码
   slaveof <master-ip> <master-port>

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

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

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

lua 复制代码
slaveof no one 

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

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

  1. 查看初始服务器角色

    可以看到初始角色role为master为主节点

  2. 设置为从服务器

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

  1. 恢复成主节点
    执行SLAVEOF no one,再次查看结果如下恢复成了主节点
相关推荐
李元豪5 分钟前
grpo nl2sql qwen3 模型强化学习训练有效果的成立条件有哪些
数据库·oracle
Hello.Reader3 小时前
RedisJSON 路径语法深度解析与实战
数据库·redis·缓存
TDengine (老段)4 小时前
TDengine 使用最佳实践(2)
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
设计师小聂!6 小时前
Linux系统中部署Redis详解
linux·运维·数据库·redis
kfepiza6 小时前
Debian-10编译安装Mysql-5.7.44 笔记250706
linux·数据库·笔记·mysql·debian·bash
Touper.6 小时前
Redis 基础详细介绍(Redis简单介绍,命令行客户端,Redis 命令,Java客户端)
java·数据库·redis
不剪发的Tony老师6 小时前
phpMyAdmin:一款经典的MySQL在线管理工具又回来了
数据库·mysql·phpmyadmin
极限实验室6 小时前
TDBC 2025 可信数据库发展大会,极限科技邀您来赴约!
数据库
lixia0417mul28 小时前
使用Starrocks替换Clickhouse的理由
数据库
张璐月9 小时前
mysql的性能优化:组提交、数据页复用、全表扫描优化、刷脏页
数据库·mysql·性能优化