目录
- 持久化
- RDB(定期备份)
- 手动触发
- save
- bgsave
- 自动触发
- 实际操作
- rdb的优缺点
- AOF(定时备份)
- 重写机制
- 混合持久化
- aof和rdb
- 总结
持久化
内存中的数据是不持久的,要想做到持久,就需要把redis中的数据存储到硬盘
redis插入数据:需要把这个数据同时写入到内存和硬盘,写入是有策略的
redis读取数据:当查询某个数据的时候,直接从内存中读取
硬盘的数据只是在redis重启的时候,用来恢复内存中的数据
代价:消耗了更多的空间,同一份数据,存储了两遍
redis实现持久化的策略
RDB(定期备份)
RDB定期把我们redis内存中的所有数据,都写入硬盘中,生成一个快照,后续redis一旦重启了,(内存中的数据没有了),就可以根据刚才的快照,把内存中的数据给恢复回来
定期有两种策略:手动触发和自动触发
手动触发
save
程序员通过redis客户端,执行特定的命令,来触发快照的生成。执行save的时候,redis就会生成快照操作,此时就会阻塞redis的其他客户端的命令,导致类似于key*的后果,所以不太建议使用save
bgsave
bg→background,不会影响redis服务器处理其他客户端的请求和命令,此处redis通过多进程的方式来完成的并发编程,具体原理如下:
- 执⾏ bgsave 命令,Redis ⽗进程判断当前进是否存在其他正在执⾏的⼦进程,如 RDB/AOF ⼦进
程,如果存在 bgsave 命令直接返回。 - ⽗进程执⾏ fork 创建⼦进程,fork 过程中⽗进程会阻塞,通过 info stats 命令查看
latest_fork_usec 选项,可以获取最近⼀次 fork 操作的耗时,单位为微秒。 - ⽗进程 fork 完成后,bgsave 命令返回 “Background saving started” 信息并不再阻塞⽗进程,可以继续响应其他命令。
- ⼦进程创建 RDB ⽂件,根据⽗进程内存⽣成临时快照⽂件,完成后对原有⽂件进⾏原⼦替换。执⾏ lastsave 命令可以获取最后⼀次⽣成 RDB 的时间,对应 info 统计的 rdb_last_save_time 选
项。 - 进程发送信号给⽗进程表⽰完成,⽗进程更新统计信息。
redis生成的rdb文件是存放在redis的工作目录中的
配置文件中有rdb的配置路径,cd /etc/redis/redis.conf,rdb文件的路径为:/var/lib/redis,默认rdb生成持久化的名字为dump.rdb,它是一个二进制文件,把内存中的数据以压缩的形式保存到这个二进制文件中(压缩的形式,会消耗一定的CPU资源,但是能节省存储空间),自己不要修改,会出错,后续redis服务器重新启动,就会尝试加载这个rdb文件。如果发现格式错误,就可能会加载数据失败
rdb文件即使我们不主动去修改它,它也可能会出错(主从复制),例如网络传输会有问题,导致这个文件被破坏
redis提供了rdb文件检查工具→redis-check-rdb*
rdb持久化操作可以触发多次,当执行生成rdb镜像操作的时候,此时就要把生成的快照数据,先保存到一个临时文件中,当这个快照生成完毕之后,再删除之间的rdb文件,把新生成的临时的rdb文件名字改成刚刚的dump.rdb
rdb文件中的数据,不是插入了数据,就会立即更新
rdb的触发时机:
1.手动(save,bgsave)
2.自动(配置文件中,进行设置)
自动触发
虽然此处的这些值可以随便修改,但是修改上述数据的时候有一个原则:生成一个rdb快照,这个成本比较高,不能让这个操作执行的太频繁了,正因为rdb生成的操作不能太频繁,这就导致,快照里的数据和当前实时的数据会存在偏差,例如save 60 10000,两次生成rdb之间的间隔,最少是60秒这就导致如果在60秒内服务器挂了,这些数据就全部丢了
实际操作
1.手动执行save&bgsave触发生成一次快照
插入几个key之后,我们使用bgsave,bgsave瞬间完成,因为这里的数据比较少,在redis服务器重启的后,会加载rdb文件的内容,恢复之前在内存中的状态
2.插入新的key,不手动执行bgsave
我们插入少量的key,让它达不到自动触发的值,使用service redis-server restart,重启以后我们会发现key依旧存在
但是如果使用kill命令去杀死就不存在
结论:如果是通过正常流畅重启redis服务器,此时redis服务器会在退出的时候,自动触发生成rdb的操作,但是如果是异常重启(kill -9或者服务器掉电等)此时redis服务器来不及生成rdb,导致数据丢失
所以redis生成快照的操作,不仅有手动触发,也可以自动触发,自动触发的几种情况
1.通过配置文件中save执行M时间内,修改N次
2.通过shutdown命令(redis里的一个命令),或者service redis-server restart命令关闭服务器也会触发
3.redis进行主从复制的时候,主节点也会自动生成rdb快照,然后把rdb快照文件内容传输给从节点(后面介绍)
3.bgsave操作流程是创建子进程,子进程完成持久化操作(难以观察到子进程,因为执行速度太快),持久化会把数据写入到新的文件中,然后用新的文件替换旧的文件(可以观察到)
可以使用stat命令,查看文件的inode编号
重点:如果直接使用save命令,此时是不会触发子进程和文件替换逻辑,如果是sav就直接在当前进程中,往刚才的同一个文件中写入数据了
Linux文件系统 文件系统典型的组织方式(ext4)主要把整个文件系统分成了三大部分
1.超级快(存放一些管理信息)
2.inode区(存放inode节点,每个节点都会分配一个inode数据结构,包含了文件的各种元数据)
3.block(存放文件的内容数据)
4.通过配置自动生成rdb快照
利用如下条件,再去看dump.rdb,会自动更改
执行flushall也会清空rdb文件
配置文件修改之后,一定要重新启动服务器,才能生效,如果想立即生效,也可以通过命令的方式修改
5.把rdb文件,故意改坏了,会如何?
手动的把rdb文件内容改坏,一定是通过kill进程的方式(如果不是kill的话,它会保存正确的结果,修改不会真正生效),然后重启redis服务器,不过redis服务器有可能看起来没受到什么影响,还是能正确获取到key,这里redis会怎么样,取决于rdb文件坏在哪里,如果改坏的地方是文件末尾,对前面的内容没什么影响,但是如果更改的是中间,那redis服务器可能就重启不了了
redis服务器挂了的时候,可以看看redis日志,在/var/log/redis路径下,查看下面的redis-server.log即可
rdb文件是二进制的,直接就把坏的rdb文件交给redis服务器去使用,得到的结果是不可预期的,可能redis服务器能启动,但得到的数据也可能有问题,也可能redis服务器直接启动失败
redis提供了rdb文件检查工具→redis-check-rdb
rdb的优缺点
- RDB 是⼀个紧凑压缩的⼆进制⽂件,代表 Redis 在某个时间点上的数据快照。⾮常适⽤于备份,全量复制等场景。⽐如每 6 ⼩时执⾏ bgsave 备份,并把 RDB ⽂件复制到远程机器或者⽂件系统中(如 hdfs)⽤于灾备。
- Redis 加载 RDB 恢复数据远远快于 AOF 的⽅式。(RDB使用的二进制方式来组织数据,直接把数据读取到内存中,按照字节的格式取出来,放到结构体/对象中,AOF是使用文本的方式组织数据,需要一系列的字节串切分操作)
- RDB ⽅式数据没办法做到实时持久化 / 秒级持久化。因为 bgsave 每次运⾏都要执⾏ fork 创建⼦进程,属于重量级操作,频繁执⾏成本过⾼。
- RDB ⽂件使⽤特定⼆进制格式保存,Redis 版本演进过程中有多个 RDB 版本,兼容性可能有⻛
险(rdb文件新老版本不兼容,我们可以写一个程序,直接遍历旧的redis中所有的key,把数据取出来放到新版本的redis中)。
AOF(定时备份)
类似于mysql的bin log,会把用户的每个操作都记录到文件中。当redis重新启动的时候,就会读取aof文件中的内容,用来恢复数据。当开启aof的时候,rdb就不生效了,启动的时候不再读取rdb文件内容了
aof默认不开启,我们需要自行设置,在/etc/redis,在redis.conf里面更改,把appendonly no改为appendonly yes
所在的目录和rdb一样,/var/lib/redis,插入数据以后,查看appendonly.aof
可以看出AOF是一个文本文件,每次的操作都会被记录到文本文件中,通过一些特殊符号作为分隔符,来对命令的细节做区分(分隔符的规则,不要研究)
引入AOF之后,又要写内存,又要写硬盘,还能和之前一样快吗?
AOF工作流程
- 所有的写⼊命令会追加到 aof_buf(缓冲区)中。
- AOF 缓冲区根据对应的策略向硬盘做同步操作。
- 随着 AOF ⽂件越来越⼤,需要定期对 AOF ⽂件进⾏重写,达到压缩的⽬的。
- 当 Redis 服务器启动时,可以加载 AOF ⽂件进⾏数据恢复。
所以实际上没有影响,原因如下:
- AOF机制并非直接让工作线程把数据写入硬盘,而是先写入一个内存中的缓冲区,积累一波之后,再统一写入硬盘
- AOF是每次把新的操作写入到原有文件末尾,属于顺序写入
如果把数据写入到缓冲区里,本质还是在内存,这个时候进程挂了或者主机掉电了,缓冲区来不及写入硬盘,数据会丢失
redis给出了一些选项,在/ect/redis/redis.conf
系统调⽤ write 和 fsync 说明:
- write 操作会触发延迟写(delayed write)机制。Linux 在内核提供⻚缓冲区⽤来提供硬盘 IO 性
能。write 操作在写⼊系统缓冲区后⽴即返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区⻚空间写满或达到特定时间周期。同步⽂件之前,如果此时系统故障宕机,缓冲区内数据将丢失。 - Fsync 针对单个⽂件操作,做强制硬盘同步,fsync 将阻塞直到数据写⼊到硬盘。
- 配置为 always 时,每次写⼊都要同步 AOF ⽂件,性能很差,在⼀般的 SATA 硬盘上,只能⽀持⼤约⼏百 TPS 写⼊。除⾮是⾮常重要的数据,否则不建议配置。
- 配置为 no 时,由于操作系统同步策略不可控,虽然提⾼了性能,但数据丢失⻛险⼤增,除⾮数据重要程度很低,⼀般不建议配置。
- 配置为 everysec,是默认配置,也是推荐配置,兼顾了数据安全性和性能。理论上最多丢失 1秒的数据。
结论:刷新频率越高,性能就影响越大,同时数据的可靠性越高,否则反之
AOF文件持续增长,体积会越来越大,会影响到redis下次启动的启动时间,因为aof文件中,有一些内容是冗余的,例如:
因此redis就存在一个机制,能够针对aof文件进行整理操作,这个整理能够去除其中的冗余操作,并且合并一些操作,达到给aof文件瘦身的效果,重写的原理:根据内存中最终数据状态重写
重写机制
分为手动触发和自动触发
- ⼿动触发:调⽤ bgrewriteaof 命令。
- ⾃动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数确定⾃动触发时机。
auto-aof-rewrite-min-size:表示触发重写时 AOF 的最⼩⽂件⼤⼩,默认为 64MB。
auto-aof-rewrite-percentage:代表当前 AOF 占⽤⼤⼩相⽐较上次重写时增加的⽐例。
AOF重写流程
- 执⾏ AOF 重写请求。
如果当前进程正在执⾏ AOF 重写,请求不执⾏。如果当前进程正在执⾏ bgsave 操作,重写命令
延迟到 bgsave 完成之后再执⾏。 - ⽗进程执⾏ fork 创建⼦进程。
- 重写
子进程写新的aof文件的同时,父进程仍然在不停的接收客户端的新的请求。父进程还是会写把这些请求产生的 AOF 数据先写入到缓冲区再刷新到原有的 AOF 文件里,在创建子进程的一瞬间,子进程就继承了当前父进程的内存状态。因此,子进程里的内存数据是 父进程 fork 之前的状态。fork 之后,新来的请求,对内存造成的修改,是子进程不知道的,此时,父进程这里又准备了一个 aof rewrite buf缓冲区,专门放fork 之后收到的数据,子进程这边,把aof数据写完之后,会通过 信号 通知一下父进程,父进程再把aof rewrite buf 缓冲区中的内容也写入到新 AOF文件里,就可以用新的 AOF 文件代替1日的 AOF 文件了
疑问:父进程fork完毕之后,就已经让子进程写新的aof文件了,并且随着时间的推移,子进程很快就写完了新的文件,要让新的aof文件代替旧的,父进程此时还在继续写这个即将消亡的旧的aof文件是否还有意义?
答:极端情况下,重写过程中,重写了一半,服务器挂了,子进程内存中的数据就会丢失,新的aof文件内容还不完整,所以父进程不坚持写旧的aof文件,重启就没办法保证数据的完整性
混合持久化
在/ect/redis/redis.conf,aof-use-rdb-preamble字段来控制是否是混合持久化,默认是yes
aof本来是按照文本的方式来写入文件的,但是文本的方式写文件,后续的加载成本是比较高的,redis就引入了“混合持久化的方式”,结合了rdb和aof的特点,按照aof的方式,每一个请求/操作,都记录入文件,在触发aof重写之后,就会把当前内存的状态按照rdb的二进制格式写入到新的aof文件中,后续在进行的操作,仍然是按照aof文本的方式追加到文件后面
aof和rdb
当redis上同时存在aof文件和rdb快照的时候,此时以aof为主,因为aof中包含的数据比rdb更安全
总结
- Redis 提供了两种持久化⽅案:RDB 和 AOF。
- RDB 视为内存的快照,产⽣的内容更为紧凑,占⽤空间较⼩,恢复时速度更快。但产⽣ RDB 的开销较⼤,不适合进⾏实时持久化,⼀般⽤于冷备和主从复制。
- AOF 视为对修改命令保存,在恢复时需要重放命令。并且有重写机制来定期压缩 AOF ⽂件
- RDB 和 AOF 都使⽤ fork 创建⼦进程,利⽤ Linux ⼦进程拥有⽗进程内存快照的特点进⾏持久化,尽可能不影响主进程继续处理后续命令。