持久化
Redis持久化就是将内存中的数据存储到硬盘中。
持久化的目的就是为了保障数据不丢失,因为内存只要一断电就会清空数据。
Redis官方提供了两种持久化机制:
- Snapshot(快照RDB)
- AOF(Append Only File只追加日志文件)
当Snapshot和AOF同时开启时,Redis会采用AOF持久化方案。
Snapshot
1. 原理
该种方式是Redis中默认的持久化方式。
在拍摄快照时将当时Redis中的数据状态记录下来,构建成 dump.rdb
快照文件存储在硬盘中。在数据恢复时执行 dump.rdb
,就可以将当时Redis中的所有数据重新装载入Redis。
2. 生成方式
- 客户端命令(BGSAVE和SAVE指令)手动生成快照。
- 修改配置,达到某条件后自动触发生成快照。
BGSAVE和SAVE指令都可以生成快照,但二者有所不同。
3. BGSAVE
官方推荐使用BGSAVE指令生成快照。
使用BGSAVE生成快照时,Redis不会阻塞。因为Redis是用主进程来处理客户端请求的,当客户端使用BGSAVE指令让Redis生成快照时,Redis会调用fork创建一个子进程,然后子进程负责将快照写入磁盘中,主进程则继续处理客户端的命令请求。
fork是类Unix系统上创建进程的函数。
当一个进程使用fork创建子进程的时候,底层的操作系统会创建该进程的一个副本,在类Unix系统中创建子进程的操作会进行优化。在刚开始的时候,父子进程共享相同内存,直到父进程或子进程对内存进行了写操作之后,对被写入的内存的共享才会结束服务。
使用fork的好处在于:因为使用fork创建的子进程会和主进程共享内存,所以当Redis使用fork创建子进程去生成快照时,主进程只要接收的都是读操作(Redis绝大部分操作都是读操作),那么父子进程就会持续共享内存,大大提高了子进程生成快照的速度。
4. SAVE
使用SAVE生成快照,Redis会阻塞。Redis服务在快照创建完毕之前,不再响应任何其他命令,因为Redis会直接使用主进程来生成快照。
5. 配置自动触发
如果用户在 redis.conf
中设置了save配置选项,Redis会在save选项条件满足之后自动触发一次BGSAVE命令。如果设置多个save配置选项,当任意一个save配置选项条件满足,Redis也会触发一次BGSAVE命令。
Redis默认配置了3个save配置选项
save配置选项的格式为:
save seconds change
例如:
save 900 1
如上命令表示,如果900s以内,只要有一个Key发生改变,就自动触发BGSAVE指令。
6. shutdown指令
当Redis服务接收到客户端的shutdown指令关闭服务时,也会自动生成快照。该生成快照底层使用的是SAVE指令,因此会阻塞Redis服务,不再执行客户端发送的任何命令,在SAVE指令执行完毕之后关闭服务器。
之所以这样设计,是因为shutdown就是明确让Redis服务关闭了,此时Redis服务就应该处于阻塞状态,不再处理任何请求了。
CTRL + C
底层调用的就是shutdown指令。
7. dump.rdb文件
dump.rdb
中持久化的数据都是以二进制的格式保存,不会显示明文内容。
dump.rdb
名称可以在 redis.conf
中修改,但是后缀必须是 .rdb
。
dump.rdb
生成位置也可以在 redis.conf
中修改,但是必须指定一个绝对或者相对路径,不能是个文件名。
默认生成位置是 redis-server
脚本启动的当前目录。
8. Snapshot缺陷
当Redis刚生成了快照之后,紧接着又响应了若干次写操作修改了部分数据,但是该写操作并没有达到触发自动生成快照的条件。如果在此时Redis忽然断电,那么此时的 dump.rdb
并没有保存由于执行了若干次写操作更新的数据,因此Redis会丢失不定量的数据,恢复也只能恢复在执行写操作之前的数据。
AOF
1. 原理
AOF持久化机制是通过将被执行的所有写命令追加到AOF文件的末尾,以此来记录全部数据生成的过程和发生的变化。在数据恢复时让Redis从头到尾执行一次AOF中记录的所有写命令,就可以恢复原来的数据。
2. 配置
AOF默认是没有开启,需要在 redis.conf
中进行配置。
AOF文件默认名称为 appendonly.aof
,存储位置和 dump.rdb
一致。
3. appendonly.aof文件
appendonly.aof
中不仅仅记录所有写命令,其实还会在写命令之间添加一些特殊符号,例如 "$" 、"*"。实际上这些符号的作用是为了充实写命令的内容,比如将该数据恢复到哪一个库中等。但是appendonly.aof
中的主体还是所有写命令。
4. 日志追加频率
AOF提供了三种日志追加频率
- always:每一个写命令都要实时同步
- everysec:每秒同步一次
- no:由操作系统决定何时同步
默认是everysec,也就是说,每过一秒都会将前一秒的写命令追加进到 appendonly.aof
中。
假设该一秒中有1000个客户端执行了写操作,那么在该秒结束后,Redis会将这1000条指令一次性追加到 appendonly.aof
中。
日志追加频率可以在 redis.conf
中进行配置
5. always
不建议配置always,因为always的同步策略会严重降低Redis的速度。
如果用户使用了always选项,那么每个Redis写命令都会被实时写入硬盘,虽然可以将发生系统崩溃时出现的数据丢失减到最少,但是这种同步策略需要对硬盘进行大量的写入操作,会导致Redis处理命令的速度受到硬盘性能的限制。
这里涉及到 "写入放大" 的知识,就是如果在某一时刻,往硬盘中不断写入小文件,会极大影响硬盘的处理速度,并且会严重影响磁盘寿命。
如果电脑使用的是磁盘当作硬盘,磁盘一般可以追加200个命令/s,如果设置了always,而每秒持续追加的命令多达200+,那么长时间硬盘就会积累越来越多的命令无法处理,最终就会将硬盘拖垮,而导致Redis服务阻塞。
如果电脑使用的是SSD当作硬盘,固态硬盘可以追加几百万个命令/s,但是也慎用always,因为 "写入放大" 的问题也会严重减少SSD的寿命。
6. everysec
为了兼顾数据安全和写入性能,推荐用户配置everysec,让Redis每秒一次的频率对AOF文件进行同步。
Redis每秒同步一次AOF文件时,性能和不使用任何持久化特性时的性能相差无几。而通过每秒同步一次AOF文件,Redis可以保证,即使系统崩溃,Redis也最多丢失一秒之内产生的数据。
7. no
no不是不同步,只是将同步的权限由Redis交给了操作系统,同样不推荐使用。
no有两个明显弊端,都是由于操作系统控制同步导致的。一个是当服务器崩溃时,会丢失不等量的数据,甚至全部数据。另一个就是因为如果Redis积攒了很长时间的写命令在操作系统的控制下进行一次性同步,此时如果硬盘处理速度还不过快,缓冲区就会被等待写入的数据填满,Redis就会处于阻塞状态。
8. AOF缺陷
AOF会使持久化文件会变的越来越大。
例如我们调用 incr test
命令100次,AOF文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实只需要保存一条 set test 100
就够了。
为了压缩AOF的持久化文件,Redis提供了AOF重写机制。
AOF重写机制
1. 作用
AOF重写机制的作用就是一定程度上减小AOF文件的体积。
2. 触发前提
必须开启AOF持久化方案,且 appendonly.aof
文件已经生成。
3. 触发方式
- 执行BGREWRITEAOF指令手动重写AOF文件。
- 修改配置,达到某条件后自动触发重写AOF文件。
4. BGREWRITEAOF
BGREWRITEAOF指令执行不会阻塞Redis,类似于BGSAVE指令。
自动触发的底层调用的也是BGREWRITEAOF指令。
5. 配置自动触发
在 redis.conf
中进行配置。
auto-aof-rewrite-percentage 100
表示当AOF文件的体积比原体积或上一次重写后的体积原来大了100%(1倍)时,触发重写。
auto-aof-rewrite-min-size 64mb
表示当AOF文件体积大于64mb时,触发重写。
这两个配置结合,当AOF文件越小,重写的次数越频繁,最终会让AOF文件始终保持一个比较小的状态。
6. 重写原理
重写并不是对原AOF文件进行压缩,而是生成一个体积更小的AOF文件对原AOF文件进行替换。
具体流程如下:
- Redis接收到BGREWRITEAOF指令后,fork出一个子进程,对当前缓存中的所有数据进行snapshot。
- 同时主进程继续处理其他请求,将新的写操作指令一方面追加到原AOF文件,一方面自己缓存一份。(追加到原AOF文件中是防止在重写的过程中忽然断电从而丢失数据,自己缓存的原因是为了后续将新的写操作指令写入临时文件)
- 当子进程snapshot成功后,发出信号通知主进程,然后主进程将缓存中存储的新的写操作指令追加到临时文件。
- 主进程将临时文件替换掉原AOF文件,之后收到的写操作指令直接追加到新的AOF文件中。
如果原AOF文件过于庞大,那么在删除原AOF文件时,也会占用系统资源,极端情况下也可能会导致Redis阻塞。