今天跟大家唠唠,Redis的持久化机制,以及在实际业务中,我们应该如何选择比较合理的方式,能够更好的解决我们的问题
1. 持久化原理
持久化:Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置
1.1 持久化流程(落盘)
既然Redis的数据可以保存在磁盘上,那么这个流程是什么样的呢?
要有下面五个过程:
(1)客户端向服务端发送写操作(数据在客户端的内存中)。
(2)数据库服务端接收到写请求的数据(数据在服务端的内存中)。
(3)服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)。
(4)操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)。
(5)磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)。
这5个过程是在理想条件下一个正常的保存流程,但是在大多数情况下,我们的机器等等都会有各种各样的故障,这里划分了两种情况
(1)Redis数据库发生故障,只要在上面的第三步执行完毕,那么就可以持久化保存,剩下的两步由操作系统替我们完成。
(2)操作系统发生故障,必须上面5步都完成才可以。
为应对以上5步操作,redis提供了两种不同的持久化方式:RDB(Redis DataBase)和AOF(Append Only File)
1.2 RDB详解
1.2.1 概念
RDB:在指定的时间间隔能对你的数据进行快照存储。
RDB持久化是将当前进程中的数据生成快照保存到硬盘(因此也称作快照持久化),保存的文件后缀是rdb;当Redis重新启动时,可以读取快照文件恢复数据。
在我们安装了redis之后,所有的配置都是在redis.conf文件中,里面保存了RDB和AOF两种持久化机制的各种配置。
1.2.2 触发&原理
在Redis中RDB持久化的触发分为两种:指令手动触发和 redis.conf 配置自动触发
指令手动触发
save命令和bgsave命令都可以生成RDB文件
save:会阻塞当前Redis服务器,直到RDB文件创建完毕为止,线上应该禁止使用。
bgsave:该触发方式会fork一个子进程,由子进程负责持久化过程,因此阻塞只会发生在fork子进程的时候。
自动触发
根据我们的 save m n 配置规则自动触发;
从节点全量复制时,主节点发送rdb文件给从节点完成复制操作,主节点会触发 bgsave;
执行 debug reload 时;
执行 shutdown时,如果没有开启aof,也会触发。
redis.conf:# 时间策略save 900 1 # 表示900 秒内如果至少有 1 个 key 的值变化,则触发RDBsave 300 10 # 表示300 秒内如果至少有 10 个 key 的值变化,则触发RDBsave 60 10000 # 表示60 秒内如果至少有 10000 个 key 的值变化,则触发RDB# 文件名称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条写入,就产生快照
下面的类似,那么为什么需要配置这么多条规则呢?因为Redis每个时段的读写请求肯定不是均衡的,为了平衡性能与数据安全,我们可以自由定制什么情况下触发备份。所以这里就是根据自身Redis写入情况来进行合理配置。
stop-writes-on-bgsave-error yes
这个配置也是非常重要的一项配置,这是当备份进程出错时,主进程就停止接受新的写入操作,是为了保护持久化的数据一致性问题。如果自己的业务有完善的监控系统,可以禁止此项配置, 否则请开启。
关于压缩的配置 rdbcompression yes
,建议没有必要开启,毕竟Redis本身就属于CPU密集型服务器,再开启压缩会带来更多的CPU消耗,相比硬盘成本,CPU更值钱。
当然如果你想要禁用RDB配置,也是非常容易的,只需要在save的最后一行写上:save ""
1.2.3 实现
手动触发bgsave方法
自动触发
1.2.4 RDB总结
优势
1、执行效率高,适用于大规模数据的备份恢复。自动备份不会影响主线程工作。
2、备份的文件占用空间小。其备份的是数据快照,相对于AOF来说文件大小要小一些。
劣势
1、可能会造成部分数据丢失。因为是自动备份,所以如果修改的数据量不足以触发自动备份,同时发生断电等异常导致redis不能正常关闭,所以也没有触发关闭的备份,那么在上一次备份到异常宕机过程中发生的写操作就会丢失。
2、自动备份通过fork进程来执行备份操作,而fork进程会将当前进程的内存数据完整的复制一份,所以这个过程占用的空间是原来的2倍,可能会导致内存不足
1.3 AOF详解
1.3.1 概念
AOF(append only file):记录每次对服务器写的操作(命令),当服务器重启的时候会重新执行这些命令来恢复原始的数据。
(默认不开启)
AOF特点:
1. 以日志的形式来记录用户请求的写操作,读操作不会记录,因为写操作才会存储2. 文件以追加的形式而不是修改的形式3. redis的aof恢复其实就是把追加的文件从开始到结尾读取 执行 写操作
1.3.2 AOF 持久化的实现
如上图所示,AOF 持久化功能的实现可以分为命令追加( append )、文件写入( write )、文件同步( sync )、文件重写(rewrite)和重启加载(load)。其流程如下:
所有的写命令会追加到 AOF 缓冲中。
AOF 缓冲区根据对应的策略向硬盘进行同步操作。
随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
当 Redis 重启时,可以加载 AOF 文件进行数据恢复。
1.3.2 开启
# 可以通过修改redis.conf配置文件中的appendonly参数开启appendonly yes # AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的。dir . # 默认的文件名是appendonly.aof,可以通过appendfilename参数修改appendfilename appendonly.aof
1.3.4 命令追加
当 AOF 持久化功能处于打开状态时,Redis 在执行完一个写命令之后,会以协议格式(也就是RESP,即 Redis 客户端和服务器交互的通信协议 )将被执行的写命令追加到 Redis 服务端维护的 AOF 缓冲区末尾。
比如说 SET mykey myvalue 这条命令就以如下格式记录到 AOF 缓冲中。
"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"
Redis 协议格式本文不再赘述,AOF之所以直接采用文本协议格式,是因为所有写入命令都要进行追加操作,直接采用协议格式,避免了二次处理开销。
1.3.5 文件写入和同步(触发)
Redis 每次结束一个事件循环之前,它都会调用 flushAppendOnlyFile
函数,判断是否需要将 AOF 缓存区中的内容写入和同步到 AOF 文件中。
flushAppendOnlyFile
函数的行为由 redis.conf 配置中的 appendfsync
选项的值来决定。该选项有三个可选值,分别是 always
、 everysec
和 no
:
always
:每执行一个命令保存一次 高消耗,最安全
everysec
:每一秒钟保存一次
no
:只写入 不保存, AOF 或 Redis 关闭时执行,由操作系统触发刷新文件到磁盘
写入 和保存概念 WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件。 SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。
1.3.6 AOF 数据恢复
AOF 文件里边包含了重建 Redis 数据所需的所有写命令,所以 Redis 只要读入并重新执行一遍 AOF 文件里边保存的写命令,就可以还原 Redis 关闭之前的状态
Redis 读取 AOF 文件并且还原数据库状态的详细步骤如下:
创建一个不带网络连接的的伪客户端( fake client),因为 Redis 的命令只能在客户端上下文中执行,而载入 AOF 文件时所使用的的命令直接来源于 AOF 文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行 AOF 文件保存的写命令,伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样的。
从 AOF 文件中分析并取出一条写命令。
使用伪客户端执行被读出的写命令。
一直执行步骤 2 和步骤3,直到 AOF 文件中的所有写命令都被处理完毕为止。
当完成以上步骤之后,AOF 文件所保存的数据库状态就会被完整还原出来。
1.3.7 AOF "重写"
问题分析:AOF采用文件追加方式,随着Redis长时间运行,会产生什么问题?
概念:
为了解决 AOF 文件体积膨胀的问题,Redis 提供了 AOF 文件重写( rewrite) 策略
如上图所示,重写前要记录名为 list
的键的状态,AOF 文件要保存五条命令,而重写后,则只需要保存一条命令。
AOF 文件重写并不需要对现有的 AOF 文件进行任何读取、分析或者写入操作,而是通过读取服务器当前的数据库状态来实现的。首先从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令,这就是 AOF 重写功能的实现原理。
触发:
rewrite的触发机制主要有:
手动调用 bgrewriteaof 命令,如果当前有正在运行的 rewrite 子进程,则本次rewrite 会推迟执行,否则,直接触发一次 rewrite
自动触发 就是根据配置规则来触发
# 重写机制:避免文件越来越大,自动优化压缩指令,会fork一个新的进程去完成重写动作,新进程里的内存数据会被重写,此时旧的aof文件不会被读取使用# 当前AOF文件的大小是上次AOF大小的100% 并且文件体积达到64m,满足两者则触发重写auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb
stat appendonly.aof 查看aof文件
1.3.8 AOF重写原理
AOF 重写函数会进行大量的写入操作,调用该函数的线程将被长时间阻塞,所以 Redis 在子进程中执行 AOF 重写操作。
在整个 AOF 后台重写过程中,只有信号处理函数执行时会对 Redis 主进程造成阻塞,在其他时候,AOF 后台重写都不会阻塞主进程。
1.4 持久化优先级
如果一台服务器上有既有RDB文件,又有AOF文件,该加载谁呢?
1.5 性能与实践
通过上面的分析,我们都知道RDB的快照、AOF的重写都需要fork,这是一个重量级操作,会对Redis造成阻塞。因此为了不影响Redis主进程响应,我们需要尽可能降低阻塞。
降低fork的频率,比如可以手动来触发RDB生成快照、与AOF重写;
控制Redis最大使用内存,防止fork耗时过长;
使用更牛逼的硬件;
合理配置Linux的内存分配策略,避免因为物理内存不足导致fork失败
线上实践经验
如果Redis中的数据并不是特别敏感或者可以通过其它方式重写生成数据,可以关闭持久化,如果丢失数据可以通过其它途径补回;
自己制定策略定期检查Redis的情况,然后可以手动触发备份、重写数据;
可以加入主从机器,利用一台从机器进行备份处理,其它机器正常响应客户端的命令;
RDB持久化与AOF持久化可以同时存在,配合使用。 好了,今天就到这里吧,我们下期再见
以下是Redis其他相关内容,欢迎点击
女朋友问我啥是多级缓存?为啥我会懵!!
OH MY GOD,原来微博、微信、购物车、抽奖小程序是这么用数据结构的!
原文:https://juejin.cn/post/7098254410723262478