持久化
Redis是一个高效的内存数据库,所有的数据都存放在内存中。我们知道,内存中的信息会随着进程的退出或机器的宕机而消失 。
为此,redis提供了两种持久化机制:RDB和AOF。这两种持久化方式的原理实际上就是把内存中所有数据的快照保存到磁盘文件上,
以避免数据丢失,默认情况下使用rdb来持久化.
RDB
原理:
RDB的主要原理就是在某个时间点把内存中所有数据保存到磁盘文件中,这个过程既可以通过人工输入命令执行,也可以让服务器周期性执行。 对于“把内存中的数据转存到磁盘中”这一过程,其实现无非就是通过定义好某种存储协议,然后按照该协议写入或读出。服务器周期执行持久化的设置是在redis.conf中设置
手动持久化的命令
- save 会堵塞服务器进程,直到rdb文件创建完为止
- bgsave 不会堵塞父进程,他是fork出一个子进程,用来达到持久化,当写完数据库状态后,新 RDB 文件就会原子地替换旧的 RDB 文件。父进程还可以处理客户端请求,在redis中一般都是使用bgsave来持久化
配置文件中设置间隔性保存
- save 900 1 如果900s中至少有1个key被修改,那么就持久化
- save 300 10 如果300s中至少有10个key被修改,那么就持久化
- save 60 10000 如果60s中至少有10000个key被修改,那么就持久化
只要满足其中一种情况,服务器就会执行 BGSAVE 命令。
rdb文件的载入
RDB文件中存放的是二进制数据,后缀名是.rdb,RDB 文件的载入是在服务器启动时自动执行的,所以没有用于载入的命令,期间阻塞主进程。 只要没有开启 AOF 持久化功能,在启动时检测到有 RDB 文件,就会自动载入。 当服务器有开启 AOF 持久化功能时,服务器将会优先使用 AOF 文件来还原数据库状态。原因是 AOF 文件的更新频率通常比 RDB 文件的更新频率高。
AOF
原理:
我们从上面的介绍知道,RDB 持久化通过保存数据库状态来持久化。而 AOF 与之不同,它是通过保存对数据库的写命令来记录数据库状态。
也就是说当你执行了 set key 123,Redis 就会将这条写命令保存到 AOF 文件中。在服务器下次启动时,就可以通过载入和执行 AOF 文件中保存的命令,来还原服务器关闭前的数据库状态了。
总体流程和 RDB 持久化一样会创建一个文件,后缀名是.aof、在服务器下次启动时就载入这个文件来还原数据.
AOF持久化的实现
AOF 持久化功能的实现可以分为 3 个步骤:命令追加、文件写入、文件同步
- 命令追加: 将命令追加到AOF缓冲区的末尾。
- 文件写入: 将AOF缓冲区的内存写到.aof文件中。
- 文件同步: 将文件保存到磁盘上。
在《Redis设计与实现》中提到,Redis 服务器进程就是一个事件循环,这个循环中的文件事件(socket 的可读可写事件)负责接收客户端的命令请求,以及向客户端发送命令结果。 当服务器是处理处理事件的时侯,可能会发生写的操作,使得一些内容会被追加到 AOF 缓冲区末尾。所以,在服务器每次结束一个事件循环之前 ,都会调用 flushAppendOnlyFile 方法。
flushAppendOnlyFile方法:执行以下两个工作:
-
WRITE:根据条件,将缓冲区内容写入到 AOF 文件。
-
SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。
-
两个步骤都需要根据一定的条件来执行,而这些条件由 Redis 配置文件中的 appendfsync 选项来决定的,一共有三个选择:
- appendfsync always 每次更新同步(sync)就写入文件中,消耗性能,安全
- appendfsync everysec 每秒执行1次 sync,可能会丢失1s的数据 (默认),较安全
- appendfsync no 不执行 sync ,这个时候由操作系统自己同步数据,速度最快,不安全
下面说下三个的区别:
- appendfsync always:每次执行完一个命令之后, WRITE 和 SAVE 都会被执行
- appendfsync everysec:SAVE 原则上每隔一秒钟就会执行一次。
- appendfsync no:每次执行完一个命令之后, WRITE 会执行,SAVE 都会被忽略,只会在以下任意一种情况中被执行:
- Redis 被关闭
- AOF 功能被关闭
- 系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行.)
既然 AOF 持久化是通过保存写命令到文件的,那随着时间的推移,这个 AOF 文件记录的内容就越来越多,文件体积也就越来越大,对其进行数据还原的时间也就越来越久。
AOF重写
创建一个新的.aof文件,新文件里面不会有冗余的数据,所以新文件比旧文件小很多
所谓冗余就是:当你使用同一个命令对某条数据改了好几次,新文件只存储最后一条数据,所以说不会有冗余的数据存在
注意:
因为要有大量的IO操作,所以redis是使用子进程来实现这个功能的,否则会导致主进程堵塞.
又有一个新的问题产生了: 子进程在重写的过程中父进程产生了新的修改命令,这就会导致数据库状态和重写后的.aof文件不一致
解决:
为了解决这个问题,Redis 设置了一个 AOF 重写缓冲区。在子进程执行 AOF 重写期间,主进程需要执行以下三个步骤:
- 执行客户端的请求命令
- 将执行后的写命令追加到 AOF 缓冲区
- 将执行后的写命令追加到 AOF 重写缓冲区
当子进程当子进程结束重写后,会向主进程发送一个信号,主进程接收到之后会调用信号处理函数执行以下步骤:
- 将 AOF 重写缓冲区内容写入新的 AOF 文件中。此时新文件所保存的数据库状态就和当前数据库状态一致了
- 对新文件进行改名,原子地覆盖现有 AOF 文件,完成新旧文件的替换.
当函数执行完成后,主进程就继续处理客户端命令,所以整个AOF重写的过程中,只有在执行信号处理的时候再回堵塞主线程,其他时候都不会堵塞
更多了解点这里.