【Redis】持久化

Redis为持久化提供了两种方式:

  • RDB:在指定的时间间隔能对你的数据进行快照存储。
  • AOF:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。

本文将通过下面内容的介绍,希望能够让大家更全面、清晰的认识这两种持久化方式,同时理解这种保存数据的思路,应用于自己的系统设计中。

  • 持久化的配置:为了使用持久化的功能,我们需要先知道该如何开启持久化的功能。
  • RDB与AOF持久化的工作原理
  • 如何从持久化中恢复数据
  • 关于性能与实践建议

一、RDB

1.1、持久化配置

# 时间策略
save 900 1  # 每900s有一次写入数据
save 300 10
save 60 10000

# 文件名称
dbfilename dump.rdb

# 文件保存路径
dir /home/work/app/redis/data/

# 如果持久化出错,主进程是否停止写入
stop-writes-on-bgsave-error yes

# 是否压缩
rdbcompression yes

# 导入时是否检查
rdbchecksum yes
  • 备份时间策略:
    • save 900 1:表示900s内如果有1条是写入命令,就触发产生一次快照,可以理解为就进行一次备份
    • save 300 10:表示300s内有10条写入,就产生快照
    • 当然如果你想要禁用RDB配置,也是非常容易的,只需要在save的最后一行写上:save “”

为什么需要配置这么多条规则呢?

因为Redis每个时段的读写请求肯定不是均衡的,为了平衡性能与数据安全,我们可以自由定制什么情况下触发备份。所以这里就是根据自身Redis写入情况来进行合理配置。
  • stop-writes-on-bgsave-error yes:这个配置也是非常重要的一项配置,这是当备份进程出错时,主进程就停止接受新的写入操作,是为了保护持久化的数据一致性问题。如果自己的业务有完善的监控系统,可以禁止此项配置, 否则请开启。
  • rdbcompression yes:关于压缩的配置 rdbcompression yes ,建议没有必要开启,毕竟Redis本身就属于CPU密集型服务器,再开启压缩会带来更多的CPU消耗,相比硬盘成本,CPU更值钱。

1.2、工作原理

关于原理部分,我们主要来看RDB与AOF是如何完成持久化的,他们的过程是如何。

1.2.1、持久化触发条件

在Redis中RDB持久化的触发分为两种:

  • 手动触发
  • Redis定时触发
1.2.1.1、手动触发
  • save:会阻塞当前Redis服务器,直到持久化完成, 线上应该禁止使用。
  • bgsave:该触发方式会fork一个子进程,由子进程负责持久化过程,因此阻塞只会发生在fork子进程的时候。
1.2.1.2、自动触发
  • 根据我们的 save m n 配置规则自动触发;
  • 从节点全量复制时,主节点发送 rdb 文件给从节点完成复制操作,主节点会触发 bgsave ;
  • 执行 debug reload 时;
  • 执行 shutdown 时,如果没有开启 aof ,也会触发;

由于 save 基本不会被使用到,我们重点看看 bgsave 这个命令是如何完成RDB的持久化的。
在这里插入图片描述
Redis的数据都是存储在内存的,所以速度才这么快;RDB是每次通过 fork 子进程全量备份,只有在 fork 的时候会造成主进程阻塞,备份过程中不会

这里注意的是 fork 操作会阻塞,导致Redis读写性能下降:

  • 我们可以控制单个Redis实例的最大内存,来尽可能降低Redis在fork时的事件消耗。
  • 适当减少自动触发的频率,减少fork次数。
  • 使用手动触发,根据自己的机制来完成持久化。

二、AOF

2.1、持久化配置

# 是否开启aof
appendonly yes

# 文件名称
appendfilename "appendonly.aof"

# 同步方式(持久化到磁盘的aof文件)
appendfsync everysec

# aof重写期间是否同步
no-appendfsync-on-rewrite no

# 重写触发配置
# 代表当前AOF文件空间 (aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的比值;其中aof_current_size和aof_base_size可以在info Persistence统计信息中查看。
auto-aof-rewrite-percentage 100
# 表示运行AOF重写时文件最小体积,默认 为64MB。
auto-aof-rewrite-min-size 64mb

# 加载aof时如果有错如何处理
aof-load-truncated yes

# 文件重写策略
aof-rewrite-incremental-fsync yes
  • appendfsync everysec 它其实有三种 持久化 模式:
    • always:把每个写命令都立即同步到 aof,比较慢,但是不会丢失数据;
    • everysec:每秒同步一次,是折中方案;
    • no:redis不处理交给OS来处理,非常快,但是也最不安全;
      一般情况下都采用 everysec 配置,这样可以兼顾速度与安全,最多损失1s的数据。
  • aof-load-truncated yes:如果该配置启用,在加载时发现aof尾部不正确是,会向客户端写入一个log,但是会继续执行,如果设置为 no ,发现错误就会停止,必须修复后才能重新加载
  • 重写策略根据 aof 文件大小和 aof 空间比例来触发,见配置说明

2.2、工作原理

AOF的整个流程大体来看可以分为两步:

  • 第一步是命令的实时写入(根据配置决定写入频率)
  • 第二步是对 aof 文件的重写。

对于增量追加到文件这一步主要的流程是:命令写入 =》追加到 aof_buf =》同步到aof 磁盘。那么这里为什么要先写入buf在同步到磁盘呢?
如果实时写入磁盘会带来非常高的磁盘IO,影响整体性能。

aof 重写是为了减少 aof 文件的大小,可以手动或者自动触发,关于自动触发的规则请看上面配置部分。fork 的操作也是发生在重写这一步,也是这里会对主进程产生阻塞。重写后的AOF文件为什么可以变小?有如下原因:

  • 进程内已经超时的数据不再写入文件。
  • 旧的AOF文件含有无效命令,如del key1、hdel key2、srem keys、set a111、set a222等。重写使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令。
  • 多条写命令可以合并为一个,如:lpush list a、lpush list b、lpush list c可以转化为:lpush list a b c。为了防止单条命令过大造成客户端缓冲区溢出,对于list、set、hash、zset等类型操作,以64个元素为界拆分为多条。

2.2.1、持久化触发条件

2.2.1.1、手动触发
  • bgrewriteaof
2.2.1.2、自动触发
  • 根据 auto-aof-rewrite-min-size和auto-aof-rewrite-percentage 参数确定自动触发时机

2.2.2、AOF 重写

下面来看看重写的一个流程图:
在这里插入图片描述
1、使用主进程重写的问题?
aof_rewrite 函数可以创建新的AOF文件,但是这个函数会进行大量的写入操作,所以调用这个函数的线程将被长时间的阻塞,因为Redis服务器使用单线程来处理命令请求;所以如果直接是服务器进程调用 AOF_REWRITE 函数的话,那么重写AOF期间,服务器将无法处理客户端发送来的命令请求;
Redis不希望AOF重写会造成服务器无法处理请求,所以 Redis决定将AOF重写程序放到子进程(后台)里执行 。这样处理的最大好处是:

  • 子进程进行 AOF 重写期间,主进程可以继续处理命令请求;
  • 子进程带有主进程的数据副本(因此上图中不需要读取旧的AOF文件,重写完成之后,直接替换旧的AOF文件), 使用子进程而不是线程,可以避免在锁的情况下,保证数据的安全性。

2、使用子进程进行AOF重写的问题

  • 子进程在进行AOF重写期间,服务器进程还要继续处理命令请求,而新的命令可能对现有的数据进行修改,这会让当前数据库的数据和重写后的AOF文件中的数据不一致。

如何修正?
为了解决这种数据不一致的问题,Redis增加了一个AOF重写缓存,这个缓存在fork出子进程之后开始启用,Redis服务器主进程在执行完写命令之后,会同时将这个写命令追加到 AOF 缓冲区和 AOF 重写缓冲区,即子进程在执行AOF重写时; 主进程需要执行以下三个工作:

  • 执行client发来的命令请求;
  • 将写命令追加到AOF缓存中;
  • 根据持久化条件,进行持久化操作到现有的AOF文件中;
  • 将写命令追加到AOF重写缓存中;
  • 将AOF重写缓存中的内容全部写入到新的AOF文件中:这个时候新的AOF文件所保存的数据库状态和服务器当前的数据库状态一致;
  • 当子进程完成对新的AOF文件重写之后,它会向父进程发送一个完成信号,父进程接到该完成信号之后,会调用一个信号处理函数,该函数完成以下工作:
    • 主进程对新的AOF文件进行改名,原子的覆盖原有的AOF文件,完成新旧两个AOF文件的替换。

当这个信号处理函数执行完毕之后,主进程就可以继续像往常一样接收命令请求了。在整个AOF后台重写过程中,只有最后的“主进程写入命令到AOF缓存”和“对新的AOF文件进行改名,覆盖原有的AOF文件。”这两个步骤(信号处理函数执行期间)会造成主进程阻塞,在其他时候,AOF后台重写都不会对主进程造成阻塞,这将AOF重写对性能造成的影响降到最低。

三、从持久化中恢复数据

数据的备份、持久化做完了,我们如何从这些持久化文件中恢复数据呢?如果一台服务器上有既有RDB文件,又有AOF文件,该加载谁呢?

其实想要从这些文件中恢复数据,只需要重新启动Redis即可。我们还是通过图来了解这个流程:
在这里插入图片描述
启动时会先检查AOF文件是否存在,如果不存在就尝试加载RDB。那么为什么会优先加载AOF呢?因为AOF保存的数据更完整,通过上面的分析我们知道AOF基本上最多损失1s的数据。

四、性能与实践

通过上面的分析,我们都知道RDB的快照、AOF的重写都需要 fork,这是一个重量级操作,会对Redis造成阻塞。因此为了不影响Redis主进程响应,我们需要尽可能降低阻塞。

  • 降低fork的频率,比如可以手动来触发RDB生成快照、与AOF重写;
  • 控制Redis最大使用内存,防止 fork 耗时过长;
  • 使用更牛逼的硬件;
  • 合理配置Linux的内存分配策略,避免因为物理内存不足导致fork失败。

在线上我们到底该怎么做?我提供一些自己的实践经验。

  • 如果Redis中的数据并不是特别敏感或者可以通过其它方式重写生成数据,可以关闭持久化,如果丢失数据可以通过其它途径补回;
  • 自己制定策略定期检查Redis的情况,然后可以手动触发备份、重写数据;
  • 单机如果部署多个实例,要防止多个机器同时运行持久化、重写操作,防止出现内存、CPU、IO资源竞争,让持久化变为串行;
  • 可以加入主从机器,利用一台从机器进行备份处理,其它机器正常响应客户端的命令;
  • 不要仅仅使用RDB,因为那样会导致你丢失很多数据;也不要仅仅使用AOF,因为那样有两个问题,第一,你通过AOF做冷备,没有RDB做冷备,来的恢复速度更快; 第二,RDB每次简单粗暴生成数据快照,更加健壮,可以避免AOF这种复杂的备份和恢复机制的bug;综合使用AOF和RDB两种持久化机制,用AOF来保证数据不丢失,作为数据恢复的第一选择; 用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB来进行快速的数据恢复
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值