注册

日常宕机?聊聊内存存储的Redis如何持久化

Redis 的数据 全部存储 在 内存 中,如果 突然宕机,数据就会全部丢失,因此必须有一套机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的 持久化机制,它会将内存中的数据库状态 保存到磁盘 中。


Redis 中的两种持久化方式: RDB(Redis DataBase)和 AOF(Append Of File)


1. RDB(Redis DataBase)


在指定的 时间间隔内 将内存中的数据集 快照 写入磁盘,也就是行话讲的快照(Snapshot),它恢复时是将快照文件直接读到内存里


1.1 原理


不使用Fork存在的问题





  • Redis 是一个 单线程 的程序,这意味着,我们不仅仅要响应用户的请求,还需要进行内存快照。而后者要求 Redis 必须进行 IO 操作,这会严重拖累服务器的性能。




  • 在 持久化的同时内存数据结构 还可能在 变化,比如一个大型的 hash 字典正在持久化,结果一个请求过来把它删除了,可是这才刚持久化结束。





Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是 最后一次持久化后的数据可能丢失


1.2 fork 函数


根据操作系统多进程 COW(Copy On Write) 机制Redis 在持久化时会调用 glibc 的函数 fork 产生一个子进程,简单理解也就是基于当前进程 复制 了一个进程,主进程和子进程会共享内存里面的代码块和数据段。




  • Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
  • 在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术
  • 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。


1.3 RDB流程图


image.png


1.4 RDB相关配置


1.4.1 配置文件



  • RDB文件默认在redis主目录下的 dump.rdb

image.png



  • 快照默认的保持策略



  1. 先前的快照是在3600秒(1小时)前创建的,并且现在已经至少有 1 次新写入,则将创建一个新的快照;
  2. 先前的快照是在300秒(5分钟)前创建的,并且现在已经至少有 100 次新写入,则将创建一个新的快照;
  3. 先前的快照是在60秒(1分钟)前创建的,并且现在已经至少有 10000 次新写入,则将创建一个新的快照;


image.png


1.4.2 相关指令


save :save时只管保存,其它不管,全部阻塞。手动保存。不建议。


bgsave: Redis 会在后台异步进行快照操作, 快照同时还可以响应客户端请求。


lastsave:获取最后一次成功执行快照的时间


image.png


1.5 RDB如何备份



  1. config get dir  查询rdb文件的目录
  2. 关闭Redis
  3. 将备份文件 dump.rdb 移动到 redis 安装目录并启动 redis 服务即可,备份数据会直接加载。

image.png


2. AOF(Append Of File)


RDB快照不是很持久。如果运行 Redis 的计算机停止运行,电源线出现故障或者您 kill -9 的实例意外发生,则写入 Redis 的最新数据将丢失。


2.1 原理


AOF(Append Of File) 是以 日志 的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来( 读操作不记录 ) , 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作


2.2 AOF流程图



  1. 客户端的请求写命令会被append追加到AOF缓冲区内;
  2. AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;
  3. AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;
  4. Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的;

image.png


2.3 AOF相关配置


2.3.1 AOF启动配置


image.png


配置文件默认关闭AOF,将配置文件设置为 appendonly yes 启动AOF。


AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)


2.3.2 AOF 同步fsync频率设置


image.png



  • appendfsync always始终同步,每次Redis的写入都会立刻记入日志;性能较差但数据完整性比较好
  • appendfsync everysec每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。
  • appendfsync noredis不主动进行同步,把同步时机交给操作系统。

借助 glibc 提供的 fsync(int fd) 函数来讲指定的文件内容 强制从内核缓存刷到磁盘。但  "强制开车"  仍然是一个很消耗资源的一个过程,需要  "节制" !通常来说,生产环境的服务器,Redis 每隔 1s 左右执行一次 fsync 操作就可以了。


Redis 同样也提供了另外两种策略,一个是 永不 fsync,来让操作系统来决定合适同步磁盘,很不安全,另一个是 来一个指令就 fsync 一次,非常慢。但是在生产环境基本不会使用,了解一下即可。


2.3.3 查看appendonly.aof文件


image.png
image.png
最后一条del non_existing_key没有追加到appendonly.aof文件中,因为它没有对数据实际造成修改


2.4 AOF如何备份


修改默认的appendonly no,改为yes


2.4.1 正常恢复



  1. config get dir  查询rdb文件的目录
  2. 关闭Redis
  3. 将备份文件 appendonly.aof 移动到 redis 安装目录并启动 redis 服务即可,备份数据会直接加载。

2.4.2 异常恢复



  1. 如遇到AOF文件损坏,通过/usr/local/bin/redis-check-aof--fix appendonly.aof进行恢复备份被写坏的AOF文件。
  2. 恢复:重启redis,备份数据会直接加载。

2.5 Rewrite重写


Redis 在长期运行的过程中,AOF 的日志会越变越长。如果实例宕机重启,重放整个 AOF 日志会非常耗时,导致长时间 Redis 无法对外提供服务。所以需要对 AOF 日志 "瘦身"
Redis 提供了 bgrewriteaof 指令用于对 AOF 日志进行瘦身。其 原理 就是 开辟 (fork) 一个子进程 对内存进行 遍历 转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件 中。序列化完毕后再将操作期间发生的 增量 AOF 日志 追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了。


2.5.1 配置文件


image.png



  • no-appendfsync-on-rewrite=yes:不写入aof文件只写入缓存,用户请求不会阻塞,但是在这段时间如果宕机会丢失这段时间的缓存数据。(降低数据安全性,提高性能)
  • no-appendfsync-on-rewrite=no:还是会把数据往磁盘里刷,但是遇到重写操作,可能会发生阻塞。(数据安全,但是性能降低)

image.png



  • auto-aof-rewrite-percentage:设置重写的基准值,文件达到100%时开始重写(文件是原来重写后文件的2倍时触发)
  • auto-aof-rewrite-min-size:设置重写的基准值,最小文件64MB。达到这个值开始重写。


例如:文件达到70MB开始重写,降到50MB,下次什么时候开始重写?100MB


系统载入时或者上次重写完毕时,Redis会记录此时AOF大小,设为base_size,


如果Redis的AOF当前大小>= base_size +base_size*100% (默认)且当前大小>=64mb(默认)的情况下,Redis会对AOF进行重写。



2.5.2 Rewrit流程图




  1. bgrewriteaof触发重写,判断是否当前有bgsave或bgrewriteaof在运行,如果有,则等待该命令结束后再继续执行。
  2. 主进程fork出子进程执行重写操作,保证主进程不会阻塞。
  3. 子进程遍历redis内存中数据到临时文件,客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区保证原AOF文件完整以及新AOF文件生成期间的新的数据修改动作不会丢失。
  4. 子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息。2).主进程把aof_rewrite_buf中的数据写入到新的AOF文件。
  5. 使用新的AOF文件覆盖旧的AOF文件,完成AOF重写。


image.png


3. 总结


3.1 Redis 4.0 混合持久化


重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。


Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是 自持久化开始到持久化结束 的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小,于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。


3.2 官方建议



  • 官方推荐两个都启用。
  • 如果对数据不敏感,可以选单独用RDB。
  • 不建议单独用 AOF,因为可能会出现Bug。
  • 如果只是做纯内存缓存,可以都
    作者:芒猿君
    来源:juejin.cn/post/7249382407245037623
    不用。

0 个评论

要回复文章请先登录注册