JAVA高级工程师--Redis持久化详细版

一、Redis  DB

Redis 数据库的数量在单机和集群模式下有根本性的区别。

1. 单机模式 (Standalone)

在单机模式下,Redis 默认提供 16 个逻辑数据库,索引编号为 0 到 15

  • 选择数据库: 使用 SELECT <index> 命令进行切换。例如,SELECT 0 使用第一个数据库,SELECT 15 使用最后一个数据库。

  • 配置数量: 这个数量可以通过修改 Redis 配置文件 redis.conf 中的 databases 16 指令来增加或减少。

  • 重要特性: 这些数据库是逻辑隔离的。它们在同一个 RDB/AOF 文件中持久化,由同一个 Redis 进程管理,但 KEYSFLUSHDBSWAPDB 等命令只对当前选中的数据库生效。

总结:单机模式默认有 16 个逻辑 DB,可配置。


2. 集群模式 (Cluster)

在官方 Redis Cluster 模式下,只有 1 个数据库,即数据库 0

  • 无法选择: 尝试使用 SELECT 命令选择任何非 0 的数据库都会报错。

  • 设计原因

    1. 简化设计:集群的核心目标是分片(Sharding),将数据分散到多个节点上。如果再支持多个逻辑数据库,会使键分布、故障转移和集群管理变得异常复杂。

    2. 命名空间隔离:在集群中,不同的业务数据应该通过键名前缀(如 user:1001order:5002) 来进行区分和隔离,而不是使用不同的数据库。

总结:集群模式强制使用且只能使用 DB 0。


对比表格

特性单机模式 (Standalone)集群模式 (Cluster)
数据库数量默认 16 个 (0-15),可配置只有 1 个 (DB 0)
SELECT 命令支持,可切换数据库不支持,使用非 0 索引会报错
设计目的逻辑隔离不同应用的数据数据分片与高可用
数据持久化所有 DB 存储在同一个 RDB/AOF 文件每个节点的 DB 0 独立存储
使用建议可用于隔离不同环境/应用的数据必须使用键前缀来隔离数据

二、Redis持久化

1.为什么需要持久化

Redis 是一个基于内存的数据库,所有数据都存储在内存中。这使得它的读写速度极快。然而,内存是易失性的,如果发生服务器断电、崩溃或进程退出,内存中的数据会全部丢失

持久化的目的就是为了解决这个问题:将内存中的数据保存到磁盘等非易失性存储中,以便在 Redis 重启后能够重新加载数据,防止数据丢失。

2.怎么持久化

redis两种持久化机制。这两种机制,其实是快照日志的形式。

(1)快照RDB (Redis Database)

就是当前数据的备份,我可以拷贝到磁盘,也可以拷贝到别的服务器,如此一来,万一现有redis的计算机节点被恶意攻击或者被人删库跑路,那么你的原有数据还是可以恢复的。像你的云服务器也有快照功能,被人攻击以后直接拿来恢复就行。它是一个紧凑的二进制文件(默认名为 dump.rdb)。是全量备份,也是Redis的默认备份方案。当redis恢复的时候会全量的恢复,此时redis处于阻塞状态。

A.优点
  • 全量备份:每隔一段时间备份,全量备份,比较适合做冷备。

  • 性能高:父进程不需要进行任何磁盘 I/O 操作,由子进程负责,对主服务性能影响极小。

  • 文件紧凑:RDB 文件是二进制压缩格式,非常适合灾难恢复备份。灾备简单,可以远程传输到其他服务器

  • 恢复速度快:恢复大数据集时,比 AOF 方式更快。

  • 父子进程相互隔离:子进程备份的时候,主(父)进程的写操作可以和子进程隔离,数据互不影响,保证备份数据的的完整性
B.缺点
  • 可能丢失更多数据:如果在下一次快照之前服务器宕机,从上一次快照之后的所有数据更改都会丢失。

  • fork 可能阻塞:如果数据集非常大,fork 子进程的过程可能会耗时较长,导致服务短暂停顿(毫秒级或秒级)。

  • 子进程内存损耗:子进程会有一定的内存消耗,尤其是当有大量新的写操作涌入的时候,那些都会有额外的内存开支
  • 全量实时备份不适用:由于定时全量备份是重量级操作,所以对于实时备份的业务场景,就不适用了。
C.RDB的保存方式

问题:假设现在是凌晨1点开始备份,由于有时间损耗,01点钟 05分产生了RDB文件保存到磁盘,那么会如下问题:

  • 这个RDB文件的内容是凌晨1点的数据?
  • 这个RDB文件内容是01点钟05分的数据?
  • 这个RDB文件内容是凌晨1点的数据,并且记录了开始 备份到结束(1点到1点05分之间)的数据?

redis有两种保存方式,一个是save命令,一个是bgsave命令

(1)save

备份rdb到磁盘,阻塞当前进程,redis不接受任何写操作,

(2)bgsave

fork(创建)一个新的子进程,子进程把rdb数据写入磁盘,写操作由父进程去处理,两个进程之间数据隔离,所以rdb的数据不会因为有新的写操作而发生变化。

新的子进程指向的缓存数据和redis父进程一致,所以速度很快。(本质是指针,指向地址,而不是复制一个新的数据,所以父进程有新的写操作是写到新的内存地址,而子进程指向的地址不变)


通过 BGSAVE 命令在凌晨 1:00 开始创建的,由于 BGSAVE 是异步操作,它需要 Fork 一个子进程。Fork 操作完成后,子进程会拥有一个与父进程(主Redis进程)在 fork 那一刻完全一致的内存数据副本。子进程随后将这个副本写入 RDB 文件。

因此,RDB 文件的内容是开始执行 BGSAVE 命令后,成功完成 fork 操作那个瞬间的数据。 fork 操作非常快(通常如此),这个时间点非常接近凌晨 1:00,我们可以近似地认为是 1:00 的数据。但从严格的技术上讲,它记录的是 fork 成功那一刻的数据。

所以上面的问题:无论是 SAVE 还是 BGSAVE,RDB 文件都代表了创建过程开始后、某个瞬间的数据。对于配置的定时任务(比如在 crontab 里设置每天 1:00 执行),我们通常会说“这是凌晨 1 点的备份数据”。

提问:既然bgsave这么好用,为啥还设计一个save命令呢?

不论是开发游戏还是普通的项目,肯定会有维护期,那么在维护期的时候,就会用到save,直接阻塞,不让新的数据写入,游戏项目是最常见的,直接停服了,所有玩家等着新版本上线后才能重新进入。

D.RDB 自动保存机制

redis的核心配置有这么一段内容,这个是触发bgsave的条件,他是自动的,满足条件就会执行rdb的备份。(要注意,它是bgsave

  • stop-writes-on-bgsave-error

    • yes:如果save过程出错,则停止写操作
    • no:可能造成数据不一致
  • rdbcompression

    • yes:开启rdb压缩模式
    • no:关闭,会节约cpu性能开支
  • rdbchecksum

    • yes:使用CRC64算法校验对rdb进行数据校验,有10%性能损耗
    • no:不校验
  • dbfilename:rdb的默认名称,可以自定 dump.rdb

(2)日志AOF (Append Only File)

A、解释和特征

AOF其实就类似于我们开发系统的时候用户的操作日志,这里指的是用户的每次写操作日志,比如增加key,修改key以及删除key,这些命令,追加式备份,都会记录下来形成一个日志文件。那么在需要恢复的时候,只需要执行这个日志文件里的所有命令,就能达到恢复数据的目的。以日志的形式记录每一个写操作命令(例如 SETSADDLPUSH),并在 Redis 重启时通过重放这些命令来恢复数据。秒级别备份。

如果追求数据的一致性,RDB会丢失最后一次的备份数据,所以往往会采用AOF来做。AOF丢失的数据会比RDB相对来说少一些。

  • 默认不开启,需要在配置文件中设置 appendonly yes

  • 所有命令追加到 AOF 文件的末尾。redis是先执行写操作指令,随后再把指令追加进aof中,追加的形式是append,一个个命令追加,而不是修改,

  • 随着写入越来越多,AOF 文件会越来越大。Redis 提供了 AOF 重写机制来压缩文件。AOF文件的大小不直接与“时间”挂钩,而是与期间发生的“写操作”的数量和大小挂钩。

  • 从头到尾恢复AOF:redis恢复的时候是读取aof中的命令,从头到尾读一遍,然后数据恢复。

  • redis恢复的时候先恢复aof,如果aof有问题(比如破损),则再恢复rdb

B、优点
  • 数据更安全:根据策略配置,最多丢失一秒的数据(everysec)。所以AOF可以每秒备份一次,使用fsync操作。

  • 可读性:AOF 文件是纯文本格式,便于理解和手动修复(虽然不推荐)。

  • 以log日志形式追加,如果磁盘满了,会执行 redis-check-aof 工具。它的核心功能是检查AOF文件的完整性,并修剪掉末尾不完整或格式错误的数据redis-check-aof --fix 命令修复AOF文件后再重启。
  • 当数据太大的时候,redis可以在后台自动重写aof。当redis继续把日志追加到老的文件中去时,重写也是非常安全的,不会影响客户端的读写操作。
  • AOF 日志包含的所有写操作,会更加便于redis的解析恢复。
C、缺点
  • 文件更大:通常 AOF 文件会比同期的 RDB 文件大。

  • 恢复速度慢:恢复大数据集时,需要重新执行所有命令,比 RDB 方式慢。

  • 性能影响:在写入负载高时,AOF 对性能的影响通常比 RDB 大。针对不同的同步机制,AOF会比RDB慢,因为AOF每秒都会备份做写操作,这样相对与RDB来说就略低。 每秒备份fsync没毛病,但是如果客户端的每次写入就做一次备份fsync的话,那么redis的性能就会下降。

D、AOF大文件问题

假如,假如某一天redis宕机挂了

  • 这个10年的AOF有多大?最大可以占用多少空间?有没有可能达到10来个T,或者更大?

按理说会,如果你吃饱了没事做,就只对某个key,新增,修改,删除,无限次的做这样的操作,持续了十来年,那么这个aof文件会很大,而且都是重复的命令。但是AOF可以压缩重写,使得体积不大。

一个积累了10年、从未进行过重写的AOF文件,对于一个写入频繁的大型系统来说,达到10TB是完全合理的,甚至可能更大。这凸显了定期执行BGREWRITEAOF(通常通过配置auto-aof-rewrite-percentageauto-aof-rewrite-min-size来自动执行)对于Redis生产运维是绝对必要的。放任AOF无限增长是一种运维事故。

  • 恢复10T文件的时候,内存不大会不会溢出?

不会。虽然文件很大,但是有效的命令实际的不会很多。而且可以压缩重写,这样体积不大,读取肯定更加快速。

  • 10T的aof恢复需要多久,有没有可能几个月,甚至1年?

按照现有情况来说,有可能吧。恢复时间极长是肯定的,但“几个月或一年”在典型的服务器硬件上是一个过于极端和悲观的估计。更可能的时间范围是几天到几周,但这完全取决于硬件性能和一个关键因素:AOF文件中的操作类型。在使用了高性能NVMe SSD,并且AOF文件是经过重写、相对精简的情况下,恢复10TB的AOF可能需要几十个小时(2-4天)

以上三点都是aof文件庞大而出现的顾虑,其实aof可以重写,对日志有一个重写机制bgrewriteaof,其实也就是瘦身的作用

E、AOF 重写 (AOF Rewrite)
  • 目的:去除冗余命令,用最小命令集合重建当前数据集状态。例如,对一个 key 先后执行了 100 次 INCR,重写后只需记录一条 SET key 100

  • 触发:手动执行 BGREWRITEAOF 或自动根据配置(auto-aof-rewrite-percentageauto-aof-rewrite-min-size)触发。

  • 过程:与 BGSAVE 类似,也是 fork 一个子进程在后台完成,不会阻塞主进程。

F、同步策略 (appendfsync):这是影响性能和数据安全性的关键配置。

  • always:每个写命令都同步刷写到磁盘。数据最安全,性能最差

  • everysec:每秒同步一次。平衡了性能和安全,是默认推荐配置。最多丢失 1 秒钟的数据。

  • no:由操作系统决定何时同步。性能最好,但数据最不安全

# AOF 默认关闭,yes可以开启
appendonly no

# AOF 的文件名
appendfilename "appendonly.aof"

# no:不同步
# everysec:每秒备份,推荐使用
# always:每次操作都会备份,安全并且数据完整,但是慢性能差
appendfsync everysec

# 重写的时候是否要同步,no可以保证数据安全
no-appendfsync-on-rewrite no

# 重写机制:避免文件越来越大,自动优化压缩指令,会fork一个新的进程去完成重写动作,新进程里的内存数据会被重写,此时旧的aof文件不会被读取使用,类似rdb
# 当前AOF文件的大小是上次AOF大小的100% 并且文件体积达到64m,满足两者则触发重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

但是这个64MB如何确认了,万一瘦身后,本来就应该高于64MB。

第一、想象一个全新的Redis实例,启动后写入了仅仅1MB的数据。如果没有这个最小size限制,只要AOF文件大小翻倍(达到2MB,满足100%的增长),就会触发重写。重写本身是有成本(fork、I/O、CPU)的,为了节省这区区1MB的空间而进行重写是得不偿失的。64MB这个值意味着Redis认为,只有当你的数据量已经达到一定规模(至少64MB)时,重写带来的收益才值得付出这个成本。

第二、64MB是Redis社区经过长期实践得出的一个经验值,它对绝大多数开发测试环境和小型生产环境都是一个安全且合理的起点。它确保了一个非常小的Redis实例不会自己折腾自己。

第三、“万一压缩后的本来就是高于64会怎么办?”这个问题其实是一个误解。触发判断依据的是当前旧AOF文件的大小,而不是重写后新AOF文件的大小“压缩后高于64MB”是正常且普遍的情况64MB只是一个触发重写的下限门槛,而不是重写后文件大小的上限。只要你的数据集本身大于64MB,重写后的文件也必然大于64MB。

第四、压缩后的长时间高于64MB,这样就会不停地触发重写,重写本身是有成本(fork、I/O、CPU)的。折旧这就说明你该调参了。

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 200MB

第五、如果AOF追加磁盘满了,会怎么样?Redis主进程在尝试执行write系统调用,向AOF文件追加命令时,操作系统会返回一个错误(如ENOSPC),告知磁盘空间已满。

(3)AOF与RDB的混合持久化(4.x后的新特性)

  • yes:AOF重写,redis会把当前所有的数据以rdb形式存入到aof中,这都是二进制数据,数据量小,随后新的数据以aof形式追加到这个aof中,那么这个aof中包含两种文件类型数据,一个是rdb,一个是aof,那么恢复的时候redis会同时恢复,这样恢复过程会更快。这相当于是一个混合体。
  • no:关闭混合模式,aof只会压缩重复的命令,这是4.x以前老版本的机制,也就是把重复的没有意义的指令去除,减少文件体积,也减少恢复的时间。

在重写AOF的时候,如果有新的命令进来要写入怎么办?那么他其实也会fork一个子进程,子进程复制重写,而新的那些写入命令会被记录到一个缓冲区,待子进程重写完毕后,缓冲工区的新的写命令会被追加到新的AOF文件中,这样就保持了数据在重写前后的一致性。

(3).修复损坏AOF文件

上面也讲了损坏情况

redis-check-aof --fix [aof文件名]
目的:删除不符合语法的指令

aof-load-truncated yes:Redis启动加载aof,命令语法不完整则修复。设置为no,Redis启动失败,需要手动用redis-check-aof 工具修复

(4)生产环境推荐策略

通常,为了在性能和安全性之间取得最佳平衡,建议同时开启 RDB 和 AOF

  1. 使用 RDB:定期做冷备,用于快速恢复和历史归档。

  2. 使用 AOF:使用 appendfsync everysec 策略,保证最多只丢失一秒的数据。

这样,在 Redis 重启时:

  • 如果 AOF 文件存在,优先加载 AOF 文件(因为它的数据更完整)。

  • 如果 AOF 文件不存在,则加载 RDB 文件。

如果说用户对redis的写操作不多甚至没有,95%以上都是读操作,那么用rdb也没啥问题。我们有一个项目是采用的缓存预热方式,用户几乎没有写操作,所以直接采用RDB就够用了,因为哪怕redis挂了,甚至RDB没了,数据还是能通过预热重新载入。
如果说你们的Redis要作为一部分的数据库来使用,那么需要用到aof,或者rdb&aof的混合模式,这样数据的完整性就更大了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值