【精通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,再次查看结果如下恢复成了主节点
相关推荐
独行soc10 分钟前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍08-基于时间延迟的SQL注入(Time-Based SQL Injection)
数据库·sql·安全·渗透测试·漏洞挖掘
White_Mountain28 分钟前
在Ubuntu中配置mysql,并允许外部访问数据库
数据库·mysql·ubuntu
Code apprenticeship29 分钟前
怎么利用Redis实现延时队列?
数据库·redis·缓存
百度智能云技术站33 分钟前
广告投放系统成本降低 70%+,基于 Redis 容量型数据库 PegaDB 的方案设计和业务实践
数据库·redis·oracle
装不满的克莱因瓶35 分钟前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
梦想平凡2 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
TianyaOAO2 小时前
mysql的事务控制和数据库的备份和恢复
数据库·mysql
Ewen Seong3 小时前
mysql系列5—Innodb的缓存
数据库·mysql·缓存
码农老起3 小时前
企业如何通过TDSQL实现高效数据库迁移与性能优化
数据库·性能优化
夏木~4 小时前
Oracle 中什么情况下 可以使用 EXISTS 替代 IN 提高查询效率
数据库·oracle