redis持久化|8月更文挑战

185 阅读5分钟

这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

前言

redis是基于内存的NoSQL数据库,一旦发生服务器宕机就会造成数据的丢失。为了防止数据丢失,redis采用了持久化技术,分别是rdbaof

一、RDB持久化

RDB持久化指的是每隔一段时间将内存中的数据集快照写入磁盘中的二进制文件(dump.rdb)。那redis是如何触发持久化快照的呢?

1.1 save

当其中一个客户端执行save命令时,redis会进行快照持久化时,redis进入阻塞状态,将不响应客户端的请求命令,直至持久化过程完成。当持久化完成后,会将新的rdb文件替换旧的rdb文件image.png 很显然,阻塞式是有弊端的,如果redis内存中有大量的数据,持久化的过程将会消耗大量的时间,期间redis将不能响应客户端,降低用户体验。

1.2 bgsave

当客户端执行bgsave命令时,redis会fork一个与主进程相同的子进程,fork期间不能响应客户端。当fork完成后,会通过子进程将内存数据进行快照持久化,期间redis主进程依然可以响应客户端。当持久化完成后,会将新的rdb文件替换旧的rdb文件

image.png 很显然,每次手动执行bgsave命令,还是非常麻烦的,毕竟我们不能时刻守在服务器旁边执行命令。所以引出了自动触发bgsave命令。

# 900s内至少执行一条写命令,触发  
save 900 1  
# 300s内至少执行10条写命令,触发   
save 300 10  
# 60s内至少执行10000条写命令,触发   
save 60 10000 

相关参数

# 持久化 rdb文件遇到错误时,主进程是否接受写入,yes 表示停止写入,如果是no 表示redis继续提供服务。  
stop-writes-on-bgsave-error yes  
# 在进行快照镜像时,是否进行压缩。yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间。  
rdbcompression yes  
# 一个CRC64的校验就被放在了文件末尾,当存储或者加载rbd文件的时候
# 会有一个10%左右的性能下降,为了达到性能的最大化,你可以关掉这个配置项。  
rdbchecksum yes  
# 快照的文件名  
dbfilename dump.rdb  
# 存放快照的目录  
dir /var/lib/redis

1.3 优缺点

(1)rbd文件是一个二进制文件,是内存数据集快照,存储上非常紧凑,文件小。所以适合备份和灾难恢复。恢复大量数据时比aof速度快。
(2)rdb文件是二进制文件,所以适合主从复制
(3)如果服务器发生宕机,会造成一段时间数据丢失

二、AOF持久化

为了弥补rdb会丢失一段时间数据的缺点,redis引入了另一种方式,那就是aof。redis服务器会将每一个写命令通过write函数追加到文件最后位置。当redis重启时,会通过重新执行文件中保存的写命令来重建内存数据库数据。

image.png

  1. 客户端发出 bgrewriteaof命令
  2. fork一个和主进程相同的子进程。
  3. 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存到 AOF重写缓冲区。这样就能保证如果子进程重写失败的话并不会出问题。
  4. 子进程根据内存快照,按照命令合并规则写入到新AOF文件中。
  5. 当子进程把内存快照写入临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
  6. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。
# 是否开启AOF,默认关闭  
appendonly yes  
# 指定 AOF 文件名  
appendfilename appendonly.aof  
# Redis支持三种刷写模式:  
# appendfsync always #每次收到写命令就立即强制写入磁盘,类似MySQL的sync_binlog=1,
# 是最安全的。但该模式下速度也是最慢的,一般不推荐使用。  
appendfsync everysec # 每秒钟强制写入磁盘一次,在性能和持久化方面做平衡,推荐该方式。  
# appendfsync no     #完全依赖OS的写入,一般为30秒左右一次,性能最好但是持久化
# 最没有保证,不推荐。  
#在日志重写时,不进行命令追加操作,而只是将其放在缓冲区里,避免与命令的追加
# 造成DISK IO上的冲突。设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,
# 等rewrite完成后再写入,默认为no,建议yes  
no-appendfsync-on-rewrite yes  
#当前AOF文件大小是上次日志重写得到AOF文件大小的二倍时,自动启动新的日志重写过程。  
auto-aof-rewrite-percentage 100  
#当前AOF文件启动新的日志重写过程的最小值,避免刚刚启动Reids时由于文件尺寸较小导致频繁的重写。 
auto-aof-rewrite-min-size 64mb 

一般我们配置appendfsync everysec,每秒持久化一次。如果服务器宕机,会造成1秒的数据丢失。

2.1 文件重写

由于aof文件存放一些写操作命令,那么会遇到文件过大的问题。如何解决这个问题呢?那就是去除一些无用的命令。redis通过数据状态重新生成一个新的aof文件(不包含无用的命令),用新的aof文件代替旧的aof文件。

// 重写前文件
incr k1 1  
set  k2 a  
set  k2 b  
incr k1 2  
incr k1 3 
set  k2 c  
del  k3  
...  
incr k1 100 

// 重写后文件
incr k1 100
set  k2 c 

多条命令可以合并为一个命令,为了防止单个命令过大造成客户端缓冲区溢出,对于list,set,hash,zset 等类型的操作,以64个元素为界拆分为多条。