跳到主要内容

Redis 内存持久化机制详解

持久化概述

Redis 作为内存数据库,数据存储在内存中,当服务器重启或宕机时,内存中的数据会丢失。为了保证数据的持久性和可靠性,Redis 提供了多种持久化机制来将内存数据持久化到磁盘。

持久化的核心价值

  • 数据安全性: 防止数据因故障丢失
  • 故障恢复: 快速恢复服务状态
  • 数据备份: 提供数据备份方案
  • 迁移支持: 支持数据迁移和部署

RDB 快照持久化

RDB 工作原理

RDB(Redis Database)通过生成数据集的时间点快照来实现持久化,将某个时刻的完整数据保存到磁盘文件中。

RDB 触发机制

RDB 快照可以通过多种方式触发:

自动触发

# redis.conf 配置示例
save 900 1 # 900秒内至少1个key发生变化
save 300 10 # 300秒内至少10个key发生变化
save 60 10000 # 60秒内至少10000个key发生变化

手动触发

# 同步执行,会阻塞Redis服务器
SAVE

# 异步执行,不阻塞服务器
BGSAVE

# 查看最后一次快照时间
LASTSAVE

自动触发场景

  • 达到配置的 save 条件
  • 执行 SHUTDOWN 命令
  • 主从复制时,主节点自动执行 BGSAVE
  • 执行 FLUSHALL 命令(可配置)

写时复制机制

RDB 使用写时复制(Copy-On-Write, COW)技术确保快照的一致性:

COW 优势

  • 内存效率: 只复制被修改的内存页
  • 性能优化: 减少 fork 时的内存复制开销
  • 一致性保证: 子进程看到 fork 时刻的数据快照
// COW 机制伪代码示例
func performBGSave() error {
// 1. fork 子进程
pid := fork()

if pid == 0 {
// 子进程:生成 RDB 快照
return generateRDBSnapshot()
} else if pid > 0 {
// 父进程:继续处理客户端请求
registerChildProcess(pid)
return nil
} else {
return errors.New("fork failed")
}
}

func generateRDBSnapshot() error {
rdbFile := openRDBFile("dump.rdb")
defer rdbFile.Close()

// 遍历所有数据库
for dbIndex := 0; dbIndex < numDatabases; dbIndex++ {
db := getDatabase(dbIndex)

// 写入数据库信息
writeDBHeader(rdbFile, dbIndex)

// 遍历所有 key-value 对
for key, value := range db.data {
writeKeyValue(rdbFile, key, value)
}
}

// 写入结束标志
writeEOF(rdbFile)
return nil
}

RDB 文件格式

RDB 文件采用紧凑的二进制格式:

RDB 优缺点分析

维度优点缺点
文件大小紧凑的二进制格式,体积小-
恢复速度恢复速度快,直接加载到内存-
数据完整性-可能丢失两次快照间的数据
性能影响频率低,对性能影响小fork 时可能短暂阻塞
适用场景备份、灾难恢复、数据迁移不适合实时性要求高的场景

AOF 日志持久化

AOF 工作原理

AOF(Append Only File)通过记录每个写操作命令来实现持久化,在服务器重启时重放这些命令来恢复数据。

AOF 同步策略

AOF 提供三种不同的同步策略,平衡性能和数据安全性:

// AOF 同步策略实现伪代码
type AOFSyncPolicy int

const (
AOF_FSYNC_NO AOFSyncPolicy = iota
AOF_FSYNC_ALWAYS
AOF_FSYNC_EVERYSEC
)

func writeAOF(command string, policy AOFSyncPolicy) error {
// 1. 写入AOF缓冲区
aofBuffer.WriteString(command + "\r\n")

// 2. 根据策略决定同步时机
switch policy {
case AOF_FSYNC_ALWAYS:
// 立即同步到磁盘
return flushToDisk()

case AOF_FSYNC_EVERYSEC:
// 写入OS缓冲区,后台线程每秒同步
return writeToOSBuffer()

case AOF_FSYNC_NO:
// 仅写入OS缓冲区,由OS决定同步时机
return writeToOSBuffer()
}

return nil
}

func backgroundAOFSync() {
// 后台线程每秒执行一次fsync
ticker := time.NewTicker(time.Second)
for range ticker.C {
if lastFsyncTime.Before(time.Now().Add(-time.Second)) {
fsync(aofFile)
lastFsyncTime = time.Now()
}
}
}

同步策略对比

策略同步时机性能数据安全性最大数据丢失推荐场景
always每个命令后立即同步最高0条命令金融、支付等关键业务
everysec每秒同步一次适中较高最多1秒数据推荐默认配置
no由操作系统决定最好最差可能丢失较多数据对性能要求极高的场景

AOF 重写机制

随着时间推移,AOF 文件会越来越大,Redis 提供 AOF 重写功能来压缩文件大小:

重写触发条件

# redis.conf 配置
auto-aof-rewrite-percentage 100 # AOF文件增长100%时触发重写
auto-aof-rewrite-min-size 64mb # AOF文件至少64MB时才考虑重写

AOF 重写流程

AOF 重写通过后台进程执行,确保不影响主进程服务:

重写过程关键点

  1. 数据一致性: 使用重写缓冲区保证重写期间的命令不丢失
  2. 原子替换: 确保AOF文件切换的原子性
  3. 最小阻塞: 只在最后替换阶段短暂阻塞主进程
// AOF重写实现伪代码
func performAOFRewrite() error {
// 1. 创建重写缓冲区
rewriteBuffer := newBuffer()

// 2. fork子进程
pid := fork()
if pid == 0 {
// 子进程:执行重写
return rewriteAOFInBackground()
}

// 3. 主进程:记录重写期间的命令
registerRewriteBuffer(rewriteBuffer)

// 4. 等待子进程完成
waitForChildProcess(pid)

// 5. 合并重写缓冲区内容
return mergeRewriteBuffer(rewriteBuffer)
}

func rewriteAOFInBackground() error {
newAOF := createTempAOFFile()
defer newAOF.Close()

// 遍历所有数据库
for dbIndex := 0; dbIndex < numDatabases; dbIndex++ {
db := getDatabase(dbIndex)

if db.isEmpty() {
continue
}

// 选择数据库命令
newAOF.WriteString(fmt.Sprintf("SELECT %d\r\n", dbIndex))

// 遍历所有key
for key, value := range db.data {
// 根据数据类型生成对应的命令
commands := generateCommands(key, value)
for _, cmd := range commands {
newAOF.WriteString(cmd + "\r\n")
}

// 处理过期时间
if expiry := db.getExpiry(key); expiry > 0 {
newAOF.WriteString(fmt.Sprintf("EXPIREAT %s %d\r\n", key, expiry))
}
}
}

return nil
}

混合持久化

Redis 4.0 引入了混合持久化,结合 RDB 和 AOF 的优势:

混合持久化配置

# 开启混合持久化
aof-use-rdb-preamble yes

# AOF重写时使用混合格式
# 文件前半部分是RDB格式的全量数据
# 后半部分是AOF格式的增量数据

混合持久化优势

  • 恢复速度快: 利用 RDB 的快速加载特性
  • 数据完整性高: 利用 AOF 的增量备份特性
  • 文件体积适中: 比纯 AOF 小,比纯 RDB 完整
// 混合持久化重写伪代码
func rewriteAOFWithRDBPreamble() error {
newAOF := createTempAOFFile()
defer newAOF.Close()

// 1. 写入RDB格式的全量数据
rdbData := generateRDBData()
newAOF.Write(rdbData)

// 2. 写入AOF格式的增量数据
incrementalCommands := getIncrementalCommands()
for _, cmd := range incrementalCommands {
newAOF.WriteString(cmd + "\r\n")
}

return nil
}

func loadMixedAOF(filename string) error {
file := openFile(filename)
defer file.Close()

// 1. 检查文件格式
if isRDBFormat(file) {
// 加载RDB部分
loadRDBFromFile(file)
}

// 2. 重放AOF部分
for {
command := readAOFCommand(file)
if command == "" {
break
}
executeCommand(command)
}

return nil
}

持久化策略选择

性能对比分析

选择决策树

生产环境推荐配置

高可用配置(推荐):

# 启用混合持久化
appendonly yes
aof-use-rdb-preamble yes
appendfsync everysec

# RDB备份策略
save 900 1
save 300 10
save 60 10000

# AOF重写配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 其他优化配置
no-appendfsync-on-rewrite no
aof-load-truncated yes

高性能配置

# 仅启用RDB
appendonly no

# 优化RDB生成频率
save 900 1
save 300 10

# 启用无磁盘复制(适用于快速网络环境)
repl-diskless-sync yes

高安全性配置

# 启用AOF
appendonly yes
appendfsync always

# 禁用RDB节省资源
save ""

# AOF重写配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

监控和运维

关键监控指标

// 持久化监控指标
type PersistenceMetrics struct {
// RDB相关
LastBGSaveStatus string // 最后一次后台保存状态
LastBGSaveTime time.Time // 最后一次后台保存时间
LastBGSaveDuration int64 // 最后一次后台保存耗时(秒)

// AOF相关
AOFEnabled bool // AOF是否启用
AOFRewriteInProgress bool // AOF重写是否在进行中
AOFLastRewriteTime time.Time // 最后一次AOF重写时间
AOFCurrentSize int64 // 当前AOF文件大小
AOFBaseSize int64 // AOF重写基准大小

// 性能指标
RDBChangesSinceLastSave int64 // 自上次保存以来的变更数
AOFBufferLength int64 // AOF缓冲区长度
AOFRewriteBufferLength int64 // AOF重写缓冲区长度
}

func collectPersistenceMetrics() *PersistenceMetrics {
info := execCommand("INFO persistence")
return parsePersistenceInfo(info)
}

常见问题排查

1. RDB 文件损坏

# 检查RDB文件完整性
redis-check-rdb /path/to/dump.rdb

# 修复策略
cp dump.rdb dump.rdb.backup
redis-check-rdb --fix dump.rdb

2. AOF 文件损坏

# 检查AOF文件完整性
redis-check-aof /path/to/appendonly.aof

# 修复AOF文件
redis-check-aof --fix /path/to/appendonly.aof

3. 持久化性能问题

# 监控命令
INFO persistence
INFO stats

# 关键指标
- rdb_last_bgsave_time_sec: RDB保存耗时
- aof_rewrite_in_progress: AOF重写状态
- aof_pending_rewrite: 等待重写的操作数

Redis 持久化机制通过 RDB、AOF 和混合持久化三种方式,为不同场景提供了灵活的数据持久化方案。合理选择和配置持久化策略,能够在保证数据安全的同时,最大化 Redis 的性能表现。

Reference