一文讲透Redis RDB持久化机制(超详细!!)

持久化简介

Redis 持久化是确保内存数据安全的关键机制,通过将数据写入磁盘避免因宕机导致数据丢失。其核心包括 RDB(快照)AOF(日志追加)混合持久化 三种模式,各有适用场景和优劣势。

RDB(Redis Database) 简介

原理:

  • 内存快照: 定期将内存数据以二进制压缩格式保存到 dump.rdb 文件中
  • 写时复制(Copy-On-Write): 主进程通过 fork() 创建子进程处理快照生成, 子进程与主进程共享内存页, 仅当数据修改时才复制内存页, 减少内存占用。

优点:

  • 特别适合数据容灾, 可以在灾难发生时轻松恢复不同版本的数据集。
  • 因为是一个压缩文件,所以可以进行远程传输提供远程救灾功能
  • 使用 Linux fork 子进程工作, 最大限度地提高了 Redis 的性能
  • 允许使用大数据集更快的重启
  • 支持重启和故障转移后的部分重新同步
  • 对数据完整性和一致性要求不高

缺点:

  • 在突发情况下发生服务中断(如:断电), 则容易丢失数据
  • RDB 需要常使用 Linux fork 子进程在磁盘中进行持久化, 若数据集很大且CPU性能优先, 则可能导致服务阻塞, 影响服务器性能。

触发机制:

自动触发:

修改 redis.conf 文件中的 SNAPSHOTTING 板块下的 save <seconds> <changes>

配置, 即规定了 多少秒内,修改了多少次数据激动进行 RDB 模式下持久化。

示例:

################################ SNAPSHOTTING  ################################

# Save the DB to disk.
#
# save <seconds> <changes> [<seconds> <changes> ...]
#
# Redis will save the DB if the given number of seconds elapsed and it
# surpassed the given number of write operations against the DB.
#
# Snapshotting can be completely disabled with a single empty string argument
# as in following example:
#
# save ""
#
# Unless specified otherwise, by default Redis will save the DB:
#   * After 3600 seconds (an hour) if at least 1 change was performed
#   * After 300 seconds (5 minutes) if at least 100 changes were performed
#   * After 60 seconds if at least 10000 changes were performed
#
# You can set these explicitly by uncommenting the following line.
#
# save 3600 1 300 100 60 10000
save 600 100 1800 10000			# 此处设置了Redis 每10分钟若进行了100次修改 或 每30分钟进行了1000次修改. 则将其自动持久化一次
修改持久化文件路径

Redis 默认的持久化文件是存储在 ./ (即 Redis 安装目录) 中的, 为了后期方便维护, 可对持久化文件存储路径进行修改: 将

dir ./
# 改为
dir /指定路径
修改持久化文件名

为了保证 Redis 的高可用, 通常在生产环境中需要以集群的方式同时部署几台 Redis服务器 , 若每个服务器的持久化文件名相同, 则可能会降低维护性, 所以建议修改 Redis 持久化文件, 修改方法如下:

# 将下行注释
dbfilename dump.rdb
# 设置此主机的 Redis 持久化文件名
dbfilename dump-6379.rdb

通过以下命令可查看是否修改成功:

127.0.0.1:6379> config get port
1) "port"
2) "6379"
127.0.0.1:6379> config get dir
1) "dir"
2) "/var/lib/redis/dumpfiles/"
使用程序触发自动持久化
# 查看备份目录
[root@redis-master ~]# ll /var/lib/redis/dumpfiles/
total 0		# 此时可以看见备份目录里没有持久化文件

编写代码使用程序触发 redis自动持久化 机制, 本文以 PythonGo 语言插入100条数据为例:

Python示例:

import redis

def test_redis(db: redis.Redis):
    for i in range(100):
        key = f"string-py{i}"
        value = f"abc-py{i}"
        try:
            db.setex(key, 48 * 3600, value)
        except redis.RedisError as e:
            print(f"Error: {e}")

if __name__ == '__main__':
    r_db = redis.Redis(host='redis_ip地址', port=6379, db=0, password='passwd')
    test_redis(r_db)

Go语言示例:

import (
	"context"
	f "fmt"
	"github.com/redis/go-redis/v9"
	"sync"
	"sync/atomic"
	"time"
)

var ctx context.Context = context.Background() // 声明 根上下文
var wg sync.WaitGroup	// 声明并发锁

type AtomicCounter struct { // 声明原子计数器类
	counter int64
}

func (a *AtomicCounter) Inc() { // 声明一个原子计数器的自增方法
	atomic.AddInt64(&a.counter, 1)
}

// 声明插入数据函数
func TestRedis(db *redis.Client, a *AtomicCounter, key, value string) {
	a.Inc()
	err := db.SetEx(ctx, key, value, 48*time.Hour).Err()
	if err != nil {
		f.Println(err)
	}
	defer wg.Done()	
}

func main() {
	var a AtomicCounter	// 原子计数器对象
	opt, err := redis.ParseURL("redis://default:passwd@redis_ip地址:6379/0")
	if err != nil {
		f.Println(err)
	}
	redisDB := redis.NewClient(opt)
	defer redisDB.Close()

	for i := 0; i < 100; i++ {
		key := f.Sprintf("string-go%d", i)
		value := f.Sprintf("abc-go%d", i)
		wg.Add(1)
		go TestRedis(redisDB, &a, key, value)
	}
	wg.Wait()
}

执行上述任意代码后可以触发 RDB自动持久化 机制, 得到以下结果:

[root@redis-master ~]# ls -lhs /var/lib/redis/dumpfiles/
total 4.0K
4.0K -rw-r--r-- 1 root root 3.2K Feb 25 18:42 dump-6379.rdb		# 备份文件大小为 3.2k

注意:

  • 若写入数据时太快, 例如写入100次数据只耗时1秒, 虽然次数已经达到自动持久化设定的次数, 但未等到后续时间窗口, 可能会导致自动持久化无法触发。
  • 使用 flushall / flushdb 清空数据库命令也会触发自动持久化, 但存储的 .rdb 文件数据为空, 无意义
  • 在生产环境中, 若产生了有数据的 .rdb 文件, 应尽快进行迁移, 使服务和备份分机隔离, 以防生产环境和备份文件一同损坏

手动触发

1. save命令

save 命令直接由 Redis 主线程执行, 会阻塞所有客户端请求, 直到 .rdb 文件生成完毕。(生产环境禁用!!!)

适用场景:

  • 程序调试或关闭服务时最后一次持久化

语法:

127.0.0.1:6379> save
OK
数据一致性风险

由于是同步操作,快照生成期间的数据修改会被包含在 .rdb 文件中,但若持久化过程中服务崩溃,可能导致最后一次快照后的数据丢失。

2. bgsave 命令

bgsave 通过 fork 子进程 异步生成 .rdb 文件, 主线程仅会产生短暂阻塞。(通过内存分片、系统调优并搭配 MQ(消息队列) 使用时,可以显著降低阻塞影响,在低谷期甚至能达到几乎无阻塞的效果!!)

注意:

  • 写时复制(Copy-On-Write)机制:子进程共享父进程内存页,仅当父进程修改数据时,系统会复制被修改的内存页,确保子进程的快照数据是执行 fork 时的状态。
  • 内存开销:极端情况下(所有共享内存被修改),内存占用可能达到原数据的 2 倍。

语法:

127.0.0.1:6379> bgsave
Background saving started
数据一致性风险

快照仅包含 fork 时刻的数据,后续修改不会被持久化,因此两次快照间的数据可能丢失。

获取最后一次生成 RDB 快照时间

通过 lastsave 命令可以查看 Redis 最后一次生成 RDB 快照的时间。

示例:

127.0.0.1:6379> lastsave
(integer) 1740485907
127.0.0.1:6379> exit
[root@redis-master ~]# date -d @1740485907
Tue Feb 25 20:18:27 CST 2025

恢复数据

当数据因意外丢失后, 可以使用持久化机制产生 .rdb 文件恢复数据, 操作如下:

1. 停止 Redis 服务

为确保恢复过程中, Redis 未运行影响恢复, 应登录 redis-cli 命令行关闭 Redis 服务, 命令如下:

127.0.0.1:6379> shutdown
not connected> exit

2.将 .rdb 文件进行迁移

将备份机中的备份 .rdb 文件迁移到 Redis 服务机上的工作目录:

[root@Bak dumpfiles]# scp dump-6379.rdb root@redis-master:/var/lib/redis/dumpfiles/

3.启动Redis服务

使用以下命令启动Redis服务, 便可恢复数据

[root@redis-master ~]# redis-server /etc/redis/redis.conf
注意:

启动 Redis服务 前请确保 Redis 服务机启动内存过度分配, 否则会出现以下警告:

[root@redis-master ~]# redis-server /etc/redis/redis.conf
3812:C 25 Feb 2025 19:29:18.139 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
启动内存过度分配:

启用方法:

[root@redis-master ~]# echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf
[root@redis-master ~]# sysctl -p

# 若输出下列内容则表示设置成功
vm.overcommit_memory = 1

值得注意的是:

启用内存过度分配后, 系统可能分配超过物理内存的空间, 需定期检查内存或对Redis 配置进行优化。

Redis 配置优化:

此处仅针对最大内存策略进行一定延伸, 若有进一步优化需求可参考Redis官方文档。

编辑 redis.conf , 设置最大内存策略:

[root@redis-master ~]# vim /etc/redis/redis.conf
# 取消下面	maxmemory <size> 的注释, 并将 size 设定为指定值
# 单机预留大约 50% 物理内存为持久化等操作预留空间, 集群主节点额外预留 10% 用于数据同步
maxmemory 4gb

配置完成重启Redis服务

[root@redis-master ~]# systemctl restart redis.service

.rdb 文件修复

若因特殊原因导致 .rdb 文件损坏, 则可使用 Redis 提供的redis-check-rdb 工具进行修复。

示例(仅对文件做出基本的检查):

[root@redis-master ~]# redis-check-rdb /var/lib/redis/dumpfiles/dump-6379.rdb
[offset 0] Checking RDB file /var/lib/redis/dumpfiles/dump-6379.rdb
[offset 26] AUX FIELD redis-ver = '7.4.2'
[offset 40] AUX FIELD redis-bits = '64'
[offset 52] AUX FIELD ctime = '1740485907'
[offset 67] AUX FIELD used-mem = '1056720'
[offset 79] AUX FIELD aof-base = '0'
[offset 81] Selecting DB ID 0
[offset 3175] Checksum OK
[offset 3175] \o/ RDB looks OK! \o/
[info] 100 keys read
[info] 100 expires
[info] 0 already expired
[info] 0 subexpires
[root@redis-master ~]#
修复模式

使用 --fix 参数尝试修复文件, 修复前应对文件进行备份

redis-check-rdb --fix /var/lib/redis/dump.rdb
输出详细信息

使用 --verbose 参数显示详细的检查过程:

redis-check-rdb --verbose /var/lib/redis/dump.rdb

禁用RDB

在某些业务中, 只使用 AOF 进行持久化, 需对 RDB快照 禁用, 下面进行操作演示:

# 编辑 redis.conf 文件
[root@redis-master ~]# vim /etc/redis/redis.conf

################################ SNAPSHOTTING  ################################

# Save the DB to disk.
#
# save <seconds> <changes> [<seconds> <changes> ...]
#
# Redis will save the DB if the given number of seconds elapsed and it
# surpassed the given number of write operations against the DB.
#
# Snapshotting can be completely disabled with a single empty string argument
# as in following example:
#
save ""		# 找到这一行, 取消注释
# 保存退出后, 重启 Redis 服务, 使配置生效
[root@redis-master ~]# systemctl restart redis.service
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值