Redis持久化机制原理分析与解惑-为什么Redis进行RDB持久化数据时,新起一个进程而不是在原进程中起一个线程

最近部门新招过来的校招生问了我一个问题:“为什么Redis进行RDB持久化数据时,新起一个进程而不是在原进程中起一个线程来持久化数据”。好吧,那今天我们就来讨论讨论Redis的持久化机制这个问题。


接下来我先直接回答这个问题,之后再去分析原因。

答案:主要是出于Redis性能的考虑,(1)Redis RDB持久化机制会阻塞主进程,这样主进程就无法响应客户端请求。(2)我们知道Redis对客户端响应请求的工作模型是单进程和单线程的,如果在主进程内启动一个线程,这样会造成对数据的竞争条件,为了避免使用锁降低性能。基于以上两点这就是为什么Redis通过启动一个进程来执行RDB了。


我们现在讨论redis的持久化机制,在Redis运行情况下,Redis 以数据结构的形式将数据维持在内存中,为了让这些数据在Redis 重启之后仍然可用,Redis 分别提供了RDB 和AOF 两种持久化模式。


首先我们来看一下Redis数据库在进行写操作时到底做了哪些事,主要有下面五个过程:

  • 客户端向服务端发送写操作(数据在客户端的内存中)。
  • 数据库服务端接收到写请求的数据(数据在服务端的内存中)。
  • 服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)。
  • 操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)。
  • 磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)。

(一)RDB持久化机制

在Redis运行时,RDB程序将当前内存中的数据库快照保存到磁盘文件中,在Redis重启动时,RDB程序可以通过载入RDB文件来还原数据库的状态。RDB机制最主要的就是rdbSave和rdbLoad函数,前者将redis内存中数据加载到磁盘上,后者将在Redis重启时将数据恢复到redis内存中,注意rdbSave会阻塞主进程。


(1)RDB持久化机制启用方式:

Ⅰ:在Redis的配置文件中开启如下设置
   save 900 1     #900秒内如果超过1个key被修改,则发起快照保存 save 300 10 #300秒内容如超过10个key被修改,则发起快照保存 save 60 10000
 
Ⅱ:也可以通过手动执行SAVE和BGSAVE命令来执行保存快照到磁盘,SAVE和BGSAVE两个命令都会调用rdbSave函数,但它们调用的方式各有不同:
SAVE 直接调用rdbSave,阻塞Redis主进程看,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。
BGSAVE 则fork 出一个子进程,子进程负责调用rdbSave ,并在保存完成之后向主进程发送信号,通知保存已完成。因为rdbSave 在子进程被调用,所以Redis 服务器在BGSAVE 执行期间仍然可以继续处理客户端的请求。
如下的伪代码演示了SAVE和BGSAVE的大体逻辑:
def SAVE():
rdbSave()

def BGSAVE():
pid = fork()
if pid == 0:
# 子进程保存RDB
rdbSave()
elif pid > 0:
# 父进程继续处理请求,并等待子进程的完成信号
handle_request()
else:
# pid == -1
# 处理fork 错误
handle_fork_error()

(2)RDB持久化机制优势
一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这样非常方便进行备份。比如你可能打算没1天归档一些数据。
方便备份,我们可以很容易的将一个一个RDB文件移动到其他的存储介质上
RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘操作。


(3)RDB持久化机制劣势
如果你需要尽量避免在服务器故障时丢失数据,那么 RDB 不适合你。 虽然 Redis 允许你设置不同的保存点(save point)来控制保存 RDB 文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。
每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。

(二)AOF持久化机制

AOF 则以协议文本的方式,将所有对数据库进行过写入的命令(及其参数)记录到AOF文件,以此达到记录数据库状态的目的。AOF文件其实可以认为是Redis写操作的日志记录文件。

(1)AOF持久化机制的启用

在Redis的配置文件中开启如下设置:
appendonly yes              //启用aof持久化方式
# appendfsync always      //每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用 appendfsync everysec //每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐 # appendfsync no //完全依赖os,性能最好,持久化没保证

我们在文章的一开始就说了,Redis数据库在进行写操作时到底做了哪些事,主要有下面五个过程,其中有两步我们是要重点考虑的:
  
  
  • (WRITE)服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)。
这一步是每当有写操作执行时,Redis就会将数据写入到操作系统的缓存中
  • (SAVE)操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)。
而这一步 的发送时机就是我们在配置文件中配置的 appendfsync 的值有关
  • no值
在这种模式下写磁盘只会在以下任意一种情况中被执行: Redis 被关闭、AOF 功能被关闭、系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行) 这三种情况下的SAVE 操作都会引起Redis 主进程阻塞。
  • everysec值
在这种模式中,SAVE 原则上每隔一秒钟就会执行一次,因为SAVE 操作是由后台子线程调用的,所以它不会引起服务器主进程阻塞。
  • always值
在这种模式下,每次执行完一个命令之后,写磁盘操作都会被执行,同时主进程会被阻塞,不能接受客户端命令请求。

(2)AOF重写机制
aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。
为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis将使用与快照类似的方式将内存中的数据 以命令的方式保存到临时文件中,最后替换原来的文件。具体过程如下

 
 
  • redis调用fork ,现在有父子两个进程
  • 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
  • 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
  • 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
  • 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。
需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。


(3)AOF持久化机制的优势
使用 AOF 持久化会让 Redis 变得非常耐久(much more durable):你可以设置不同的 fsync 策略,比如无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。 AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据( fsync 会在后台线程执行,所以主线程可以继续努力地处理命令请求)。

(4)AOF持久化机制的劣势
对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。




评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值