redis 是内存级数据库,其数据存储在内存中,因此能够提供快速的读写速度。但我们知道内存属于掉电易失存储器,一旦断电,存储在内存中的数据就会丢失。
在服务器重启和 redis 服务重启后数据都会丢失。因此 redis 提供了三种持久化方式:rdb(快照持久化),aof(追加文件持久化),混合持久化(混合使用 aof 日志和内存快照)。其中aof 文件的内容是操作命令,rdb 文件的内容是二进制数据。
rdb快照
rdb快照是一种将redis数据库状态保存到磁盘上的机制,redis提供了两个命令:save , bgsave。
这两个命令实际上都会生成一份rdb文件,rdb存储了执行命令时的 redis 的所有数据,在 redis 启动时,会自动加载rdb文件恢复数据。redis并没有手动加载rdb文件的命令。
save与bgsave
save 命令是在主线程下执行,由于和执行操作命令在同一个线程,所以如果写入 rdb 文件的时间太长,会阻塞主线程。
bgsave 命令会创建一个子进程执行 save 命令,主进程不会阻塞。
redis可以通过配置文件的选项来实现每隔一段时间自动执行一次 bgsava 命令:
save <seconds> <changes>
- <seconds>:自上次保存后经过的时间(以秒为单位)。
- <changes>:在这个时间段内至少有多少个键被改变。
使用示例: 上次快照以来有 60 秒并且至少有 1000 个键被改变,则自动执行 bgsave。
save 60 1000
我们可以设置多个 save ,只要有一个条件满足就会执行 bgsave。
save 900 1 save 300 10 save 60 10000
bgsave 会先 fork 创建出子进程,在 linux 中 fork 创建出的子进程会复制父进程的页表,与父进程共享一份物理内存。
这种方式减少了创建子进程时的性能损耗,从而加快创建子进程的速度,避免主进程阻塞。创建子进程后,子进程只读,同时将数据存储在 rdb 文件中。
当执行命令的主线程内存数据也都是只读操作时,主线程和 bgsave 子进程相互不影响。当主进程对内存数据修改时会触发中断,中断处理程序会申请一份物理内存重新映射到修改的虚拟内存,这个过程叫做写时拷贝。
因此,当主进程执行写操作时,对数据的修改不会影响到子进程,子进程持久化的仍是执行 fork 命令时刻的数据。 因此会产生数据丢失。
在极端情况下,如果所有的共享内存都被修改,则此时的内存占用是原先的 2 倍。所以,针对写操作多的场景,我们要留意下快照过程中内存的变化,防止内存被占满了。
rdb优缺点
rdb优点:
- 快速恢复: rdb 文件是二进制格式,可以非常快速地加载到内存中,实现redis服务的快速启动。
- 数据备份: rdb 文件可以很容易地被复制到其他服务器或备份存储中,适合于进行数据备份和灾难恢复。
- 空间紧凑: 由于rdb文件只包含最终的数据,而不是每一条写命令,所以通常比aof文件更紧凑。
- 减少i/o操作: 相比于aof持久化,rdb不需要记录每一条写操作命令,因此减少了i/o操作。
- 适合大规模数据恢复: 由于rdb文件包含了数据集的全量快照,适合于大规模数据的恢复。
rdb缺点:
- 数据丢失风险:如果两次快照之间redis发生故障,那么这段时间内的数据将丢失。
- 数据完整性不高:rdb无法保证数据的完整性和一致性。
- 阻塞:尽管 bgsave 是在后台异步执行的,但在生成rdb文件时,如果数据集非常大,仍然可能会短暂地阻塞主线程。
- 不适合高频率写操作的场景:在写操作非常频繁的场景,会频繁生成rdb快照对性能产生影响。
aof日志
aof日志持久化并不是直接保存 redis 中的数据,而是记录redis中执行的所有写操作命令。每当redis执行一个写操作命令,该命令会被追加到aof文件中。
在 redis 中 aof 持久化功能默认是不开启的,需要我们修改 redis.conf 配置文件中的以下参数
- appendonly yes:开启aof持久化。
- appendfilename "appendonly.aof":指定aof文件的名称。
redis 会先执行命令,执行命令成功后再将命令记录到 aof 日志中,如果先写入 aof 文件还需要检查命令是否执行成功,如果命令执行失败还需要额外操作。先执行命令也保证了aof 不会阻塞当前写操作命令的执行(但可能会阻塞下一个命令的执行),因为当写操作命令执行成功后,才会将命令记录到 aof 日志。
aof持久化,会先将命令写入内存缓冲区中。这个内存缓冲区称为 aof 缓冲区。然后再通过 wirte系统调用写入到磁盘上的 aof 文件。但 wirte写入实际上是将数据拷贝到内核缓存区,由内核决定何时进行磁盘io。如果系统宕机,而缓冲区数据没有写入磁盘,这部分数据就会丢失。
aof写回策略
redis可以通过fsync或fdatasync系统调用来请求操作系统将内核缓存区中的数据强制刷新到磁盘上。
aof提供了三种写回策略: always ,everysec , no。
- always:每次写操作命令执行完后,同步将 aof 日志数据写回硬盘。即每次执行后都调用 fsync。
- everysec :每次写操作命令执行完后,先将命令写入到 aof 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘,即每秒执行一次 fsync 。
- no: 不由 redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 aof 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。即不执行 fsync 。
这三种策略各有优缺点, always 可靠性最强性能较差,no 性能最强可靠性最差,everysec则更为折中。大家可以根据自己的需要选择策略。
aof重写
aof日志是一个文件,随着执行的写操作命令越来越多,文件的大小会越来越大。如果 aof 文件过大必然会影响性能。因此 redis 提供了 aof重写机制。
当 aof 文件的大小达到一定阈值时,redis 会触发 aof 重写过程。这个阈值可以通过配置设置。
auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb
- auto-aof-rewrite-percentage:指定 aof 文件大小相对于上一次重写后大小的增长百分比,当超过这个百分比时,redis 将触发 aof 重写。
- auto-aof-rewrite-min-size: 这个配置项指定了 aof 文件的最小大小,只有当 aof 文件的大小超过这个值时,auto-aof-rewrite-percentage 指定的百分比阈值才会生效。
什么是重写?aof 文件记录了每一个写操作命令,如果记录了对同一数据的多个操作,实际上只有最后一个操作是有意义的,其他操作都是冗余的。aof 重写机制通过创建一个新的 aof 文件来解决这个问题,新文件只包含恢复当前数据集所需的最小命令集合。
在重写时 redis 会通过 fork() 系统调用创建一个子进程,这个子进程与父进程共享相同的内存空间。子进程会遍历 redis 数据库中的所有键,并生成一系列可以恢复这些数据的 redis 命令,同时将这些命令写入到一个临时的 aof 文件中。
重写完成后,子进程会用新的 aof 文件替换旧的 aof 文件,并更新 aof 文件的文件名。
在重写过程中,主进程并不会阻塞而是继续处理客户端请求,同时将执行后的写命令追加到 「aof 缓冲区」与「aof 重写缓冲区」。
当重写完成时,会向主进程发送一条信号,主进程收到该信号后,会调用一个信号处理函数,该函数会做以下工作:
- 将 aof 重写缓冲区中的所有内容追加到新的 aof 的文件中。
- 新的 aof 的文件进行改名,覆盖现有的 aof 文件。
然后主进程会继续接收命令并执行,重写也就此完成。
aof优缺点
aof优点:
- 数据安全性高: aof 记录了所有写操作,可以提供更好的数据恢复保证。
- 数据完整性: aof 能够保证即使在 redis 异常终止的情况下,也不会丢失任何已持久化的写操作。
aof缺点:
- 磁盘空间使用: aof 文件可能会比 rdb 文件大,因为它记录了所有的写操作命令。
- 性能影响: 频繁的磁盘i/o操作可能会影响 redis 的性能。
- 数据恢复速度: 由于 aof 文件可能包含大量的命令,数据恢复速度会比 rdb 慢。
混合持久化
我们可以看到rdb与aof各有优缺点:rdb 恢复速度快,aof 丢失数据少?redis 4.0 引入了一种新的持久化方式,称为混合持久化,它结合了 rdb和 aof持久化的优点。
我们可以通过配置文件开启混合持久化:
aof-use-rdb-preamble yes
当开启了混合持久化时,在 aof 重写时,重写子进程会先将与主线程共享的内存数据以 rdb 方式写入到 aof 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 aof 方式写入到 aof 文件,写入完成后通知主进程将新的含有 rdb 格式和 aof 格式的 aof 文件替换旧的的 aof 文件。
当 redis 重启时,它会首先加载 rdb 文件快速恢复数据状态。 然后,redis 会执行保存在 aof 文件中的写操作命令(这里的内容是 redis 后台子进程重写 aof 期间,主线程处理的操作命令。),恢复到最新的数据状态。
混合持久化的主要优点是结合了 rdb 和 aof 的优点,提供了更快的数据恢复速度和更少的数据丢失风险。在 aof 重写过程中,redis 确保了数据的完整性和一致性,同时优化了磁盘空间的使用。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论