您的位置:首页 > 数据库 > Redis

【Redis笔记】一起学习Redis | 聊聊Redis的持久化策略,AOF和RDB

2019-08-02 11:06 836 查看
版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

聊聊Redis的持久化策略,AOF和RDB

如果觉得对你有帮助,能否点个赞或关个注,以示鼓励笔者呢?!博客目录 | 先点这里

  • 前提概念

    持久化的作用
  • Redis持久化的方式
  • RDB持久化策略

      什么是RDB?
    • RDB的两种策略方式
    • 如何使用RDB策略备份数据?
    • RDB的原理是什么?
  • AOF持久化策略

      什么是AOF
    • AOF的原理是什么
    • AOF的三种策略
    • AOF的重写机制
  • RDB和AOF的抉择

      RDB还是AOF?
    • Redis4.0的混合持久化
    • 运维最佳实践
  • 其他问题

      RDB持久化操作时,子进程拷贝父进程的数据副本用于持久化,不会增加内存消耗吗?
    • 为什么Redis的AOF机制是先成功命令才再记录日志?

    前提概念

    持久化的作用

    什么是持久化?

    • 我们将能把内存中的数据保存到磁盘中存储的行为就称之为
      持久化

    为什么需要持久化?

    • Redis所有的数据保存在内存中,如何Redis实例突然的宕机,其占用的全部内存就会被系统释放,导致Redis的数据全部丢失,因此必须要有一种机制来保证Redis的数据不会因为故障而丢失。这一机制就是Redis的持久化机制。

    所以持久化操作的作用就是把Redis中的数据,保存到磁盘中。避免因为实例宕机,而出现数据丢失的现象

    Redis持久化的方式

    Redis为我们提供了主要的两种持久化方式

    • 数据快照 (RDB)

      这种行为就类似MySQL的Dump ,redis的RDB,这就是某时某个点的全部数据备份
    • 日志文件(AOF)

      每次的操作就写入日志,当我们需要恢复数据时,就可以根据日志的记录完整的走一遍流程,恢复数据,比如MySQL的BinLog,Hbase的HLog,Redis的AOF

    RDB持久化策略

    什么是RDB?

    什么是RDB数据快照?

    • RDB文件是一个二进制文件,存储在硬盘当中。其实就是之前用过的MySQL dump,我们把某个时刻的数据导出成RDB文件,需要的时候,导回去恢复数据即可

    RDB的两种策略方式

    • save(同步)

      我在redis client执行save命令,它就会在Redis安装目录下生成一个RDB文件,在数据量比较大的时候,会造成一个同步阻塞。如果存在老的RDB文件,则替换。时间复杂度是
      O(n)

    • bgsave命令(异步)

      使用了linux的fork()函数生成了主进程的子进程,让这个子进程去完成RDB的生成。生成完毕后,会通知主进程,我们的RDB文件生成成功了。时间复杂度也是O(n),只不过不阻塞主进程

    如何使用RDB策略备份数据?

    支持自动,手动两种方式进行RDB持久化
    redis可以通过客户端主动持久化,输入命令,生成RDB文件,也可以通过配置来等满足条件时自动持久化,生成RDB文件

    通过客户端输入config get dir就可以知道Redis的数据存放路径。
    通过客户端输入config get dbfireaname就可以知道rdb文件的名称

    • 手动持久化

      手动在客户端输入save 或 bgsave命令
    • 自动持久化

      通过配置,使得Redis在某个条件的时候自动触发RDB文件的生成,比如
      save m n
      配置
      save 900 1
      900秒内至少有1个key被改变则做一次快照
      save 300 10
      300秒内至少有300个key被改变则做一次快照
      save 60 10000
      60秒内至少有10000个key被改变则做一次快照

    什么情况下会主动触发Redis的RDB快照机制呢?

    • 主从复制时,从库全量复制同步主库数据,此时主库会执行bgsave命令进行快照;
    • 客户端执行数据库清空命令FLUSHALL时候,会触发快照的生成
    • 客户端执行shutdown关闭redis时,会触发快照的生成

    RDB的原理是什么?

    (一) RDB文件如何生成?

    • RDB快照文件即使就类似MySQL的DUMP文件,它就是一个快速数据,对某个时间节点的数据来个快照,然后备份到磁盘中。
    • 说白了当你要进行快照持久化时,Redis就会遍历内存中的所有数据,然后将数据序列化后写入到磁盘中的RDB文件

    (二) 单线程模型的Redis进行RDB持久化,难道不会阻塞其他业务请求吗?
    我们知道RDB持久化策略就是Redis在某个时间节点,扫描内存中的所有数据,生成一个二进制RDB文件,保存到本地。但是我们知道Redis是单线程模型的进程,既单个线程在处理客户端请求的同时,还要花点时间处理持久化生成RDB文件的耗时操作。那么问题就来了,生成RDB文件如此耗时,那么持久化的过程不就会阻塞我们正常的业务请求了吗?

    答案是肯定会的,所以Redis为了解决这个问题,采用的是操作系统中的

    多进程COW机制 (Copy On Write)
    来实现快照持久化

    (三) 多进程COW机制 (Copy On Write)原理

    什么是Copy On Write?

    • 相信Copy On Write这个名词,应该很多开发人员都知道,其作用就像Java中的CopyOnWriteArrayList的数据结构一样。当有人要写一个cow对象时,我们不会直接在该对象进行操作,而是复制一份它的副本数据,对该副本进行写操作,最后将修改后的副本替换原数据。它的作用就是可以实现对cow对象做到并发读-写不冲突,读-读不冲突。唯有写写才冲突

    那什么又是多进程Copy On Write呢?

    • 我们都知道Redis是单线程模型,意思就是单个进程单个线程。但是实际上,Redis并非真的是单进程/线程的去处理事情。既主要的工作的确是单进程/线程模型,但也有一些很重要的,但比较耗时的操作,Redis并不是交给主进程/线程去做的,而是扔个子进程/线程去做异步操作!!
    • 既Redis在持久化时会调用glibc的函数,从主进程中fork出一个子进程,RDB快照持久化操作就是完全交给子进程异步去实现,而父(主)进程依然正常的继续处理客户端的业务请求。这里描述的就是多进程的体现
    • 而Copy On Write的体现就在于,RDB持久化操作,生成的是某一个时间节点的数据。比如我在12:00开始生成快照,那么不管之后Redis是否又对内存的数据做了什么操作,但我的RDB数据快照就一定是12:00这个时刻的数据。所以如果在12:00之后,还有其他客户端的写操作请求,那么Redis的父进程就会拷贝一份12:00的完整副本数据,专门用于做数据修改,而原数据则被子进程用于做RDB持久化的操作。所以主线程在12:00之后对数据的增删查改,是不会反应到快照数据中的。

    AOF持久化策略

    什么是AOF

    什么是AOF?

    • AOF就是一个日志文件,对Redis的每一条命令都会以AOF格式写入AOF文件中。需要恢复时,就导入AOF文件去执行里面的记录就可以了,而且记录肯定是实时的,毕竟每执行一条命令就会有相应的纪录
    • AOF日志存储的是Redis服务器的顺序指令序列,AOF日志只记录对内存进行修改的指令
    • AOF日志的记录并不像MySQL, HBase等数据库先写入日志再执行操作,而是先执行成功命令,再将命令记录到日志文件中。
    • 通过在配置文件配置appendonly参数为yes, 以开启AOF持久化

    AOF的三种策略

    (一) AOF写入原理
    我们知道了Redis的AOF文件实际就是记录Redis实例的所有涉及修复内存的指令,但是我们要记录一个记录日志的特性

    • 但是这些修改指令的记录并不是直接刷到磁盘文件中的,而是①首先将内容写入内核的内存缓冲区,②然后再根据策略从内核异步刷新到硬盘的AOF文件中。
    • 这是为什么呢?因为这是一个效率问题,如果直接写入硬盘,速度较慢,有瓶颈。所以先写入缓冲区,保证吞吐量,然后再异步的刷新到硬盘中

    (二) AOF写入的三种策略
    所以Redis给我们提供了三种AOF策略,以供用户按需选择

    • always

      实时刷新,将缓冲区的每一条命令都立即刷新到硬盘的AOF文件。既每执行一条命令,待其刚刷入缓冲区,就立即强制刷入磁盘AOF文件中

    • everysec

      每一秒执行一次,既每一秒都强制将缓冲区的内容刷新到硬盘AOF文件中。

    • no

      不强制刷入,no就是不需要我们来考虑什么时候刷入磁盘,而是由操作系统来决定,他想什么时候刷新就什么时候刷新

    (三) 三种策略的优缺点比较

    • always
      实时刷新策略的好处是可以保证,AOF文件是实时更新的,可以保证AOF日志肯定是完整的,不会出现AOF日志内容丢失的情况。但是缺点也很大,就是每次写操作,就要立马刷入日志,使得Redis的内存操作者和磁盘IO操作被同步捆绑,以至于产生大量的同步IO操作,造成Redis的写性能严重下降。影响了Redis高性能的口碑
    • everysec
      每秒一次策略,可以保证AOF文件每秒都能刷入一批指令记录。既在安全性和性能上进行平衡考虑的折中策略。好处就是在保证性能的同时,一定程度的保证数据的持久性,最多丢失1s的数据。缺点就是在高并发的场景下,1s中也可能会产生大量缓存数据,丢失1s的重要数据可能会造成生产事故
    • no
      这个机制比较少用,因为基本上都不能用在生成环境中。因为同步日志的时间间隔不能确定,完全靠操作系统自主决定。所以一旦宕机,可能会丢失一段时间的数据。

    AOF的原理是什么

    (一) AOF日志文件生成的原理?
    AOF日志存储的是Redis服务器的顺序指令序列,AOF日志只记录对内存进行修改的指令。 假如AOF日志记录了自Redis实例创建以来的所有修改性指令,那么就可以通过对一个空的Redis实例顺序执行AOF日志中的所有指令,以“重放”的行为来复制或恢复整个Redis的内存数据结构状态。

    • Redis在收到客户端的修改指令后,会进行参数校验,逻辑处理的操作,在没有发生错误的情况下,就会立即将该指令写入AOF日志文件中。这里不同于MySQL先写入biglog,再执行命令的顺序,Redis是先执行命令成功后,才写入日志的。
    • Redis在长期运行的过程中,AOF的日志会越来越长。如果实例宕机重启,重放整个AOF日志的操作会非常的耗时,可能会导致Redis长时间无法对外提供服务,所以需要定期对AOF日志进行相应的瘦身

    (二) fsync是什么鬼!?

    我们知道AOF日志是以文件的形式存储在磁盘中,但是当Redis对AOF进行日志写操作的时候,①实际是先将内容写到内核为该文件描述符分配的一个内存缓存中,②然后内核会异步的将数据刷到入磁盘文件

    所以一旦机器突然宕机,如果AOF日志此时如果还有部分还在内核缓存中的内容没刷入文件的话,也就会产生AOF日志丢失的问题。我们知道也了解到了AOF机制又三种策略,每种策略导致的持久化效果有所不同,造成的数据丢失影响也不同。而造成他们的唯一区别就是 “内核什么时候将数据刷入磁盘文件” 。那么AOF三种策略是如何去影响内核什么时候刷入磁盘文件的呢?

    fsync函数的作用

    • 这就是
      fsync
      函数的作用啦!!Linux的glibc提供了
      fsync(int fd)
      函数,其可以将指定文件的内容强制从内核缓存刷入磁盘。
    • always策略实际就是每执行一个命令,就执行一次fsync函数。而everysec策略则是定时每秒执行一次fsync含。no则是不主动干预操作系统,完全放任系统自身去调节这个问题,一般就是等内核的缓冲区满了之后,再刷入磁盘

    AOF的重写机制

    为AOF日志文件进行瘦身

    • 我们知道,随着时间的逐步推移和命令的写入,AOF文件会越来越大,此时AOF文件就会显得非常臃肿,每次重放AOF日志时,也需要耗时非常大。那怎么办呢?有什么好的办法解决这个问题呢?答案还是有的,那就是对AOF日志文件进行
      “重写”
    • AOF日志重写的目的很明确,就是
      减少硬盘占用量
      加速日志重放速度

    为什么重写可以瘦身呢?

    • 比如我们先将
      key=a
      的值修改
      1
      , 然后再将
      a
      的值修改为
      2
      ,依次循环,每次修改
      a
      值+1
      ,总共循环1000次,那么最终
      键a
      的值就为
      1000
      。其对应的意思就是说AOF日志文件中也有1000条修改指令。如果我们拿它进行重放操作,最终Redis的值也是1000。
    • 但是有没有什么办法,可以将日志文件中的1000条修改记录削减一下,也能重放时,让键a的值等于1000呢? 有啊,很简单啊,那就是直接写入一个命令让键a的值等于1000不就行了吗?这样AOF日志也只有一个记录了,从
      1000 -> 1
      ,大削减压!!
    • 没错,重写AOF日志就是这样的一个过程,因为就AOF日志作用的本质来说,它更在意的是结果,而不是过程。既不管通过什么样的过程进行重发,只要最后的结果是正确的,那就没问题了。

    重写AOF的原理 ? !

    • Redis提供了
      bgrewriteaof
      指令,用于对AOF日志进行瘦身,其原理就是开辟一个子进程对当前内存数据进行遍历,然后转换成一系列的Redis的操作指令,序列化到一个
      新的AOF日志文件
      中。序列化完毕后再将操作期间发生的
      增量AOF日志
      追加到这个新的AOF日志文件中,追加完毕后,立即使用
      新的AOF文件
      去替换
      旧的AOF文件
      。至此瘦身工作完成

    RDB和AOF的抉择

    RDB还是AOF?

    (1) RDB模式的优缺点
    优点:

    • RDB快照非常适合做数据备份,因为RDB数据文件非常的紧凑,同样的数据大小,相比AOF日志会更小,节省磁盘空间。
    • RDB快照恢复起来,也比AOF日志要更快

    缺点:

    • 生成RDB文件,通常是遍历整个内存数据块,所以即使是fork的子进程去做,也需要一定的资源消耗,时间可能会比较长。
    • 因为生成一个完整的RDB快照数据文件,需要一定的时间,比如几分钟。而且在RDB快照生成期间,此时如果有数据的改动,是不再会反应到RDB文件中的,所以一但数据库宕机,就可能丢失几分钟的数据。既数据安全性没有AOF日志高
    • RDB数据文件,可读性差,不能直接打开

    (2) AOF模式的优缺点
    优点:

    • AOF支持三种持久化策略,可以使用fysnc函数强制内核刷新缓存到AOF日志中,相比RDB模式,AOF可以更好的保护数据不丢失。
    • AOF日志文件可读性好,可以使用文本工具直接打开,执行了什么修改指令, AOF日志就记录什么。方便查看

    缺点:

    • 对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大。
    • 通过AOF日志文件进行数据恢复所需要的时间会更长,相比RDB模式,宕机的Redis实例恢复时间过长,就需要耽搁一段时间

    (3) 选择?!

    • 如果你只是需要一个数据备份,不太在乎一小部分数据的丢失,那么你可以使用RDB模式。并且RDB文件也便于迁移,放到多个实例中进行数据恢复
    • 如果你相比之下,更在乎数据的完整性,既更看重数据的不丢失,那么你就可以使用AOF模式

    Redis4.0的混合持久化模式

    我们知道,如果采用RDB持久策略,一旦Redis宕机,就有可能丢失大量数据。但如果我们使用AOF持久策略,使用AOF日志重放的方式,一旦AOF日志大了,想比RDB的恢复耗时就显的非常慢。所以为了解决这样纠结的问题,Redis 4.0 带来了一个新的持久化特性

    “混合式持久化”


    什么是混合式持久化策略?

    • 混合式持久化策略,就是不再是单独的RDB快照模式或AOF日志文件模式,而是同时采用RDB快照和AOF日志两种持久化模式的混合持久化模式。具体表现就是会将RDB文件内容和增量的AOF文件内容都存放在同一个文件中(同一个aof格式文件)。
    • 因为RDB快照数据和AOF日志数据都存在同一个文件中,所以该aof格式文件就不再只存放全量的AOF日志了,而是前面的大部分存储的是RDB的快照数据,后一小部分存储的是自RDB快照持久化开始到持久化结束的这段时间发生的增量AOF日志。所以通常是RDB数据占了大头,AOF日志只是一小部分的增量日志
    • 在混合式的持久化策略下,当宕机的Redis实例重启时,会先加载RDB快照的内容,然后再重放增量的AOF日志。这样就可以替代之前的RDB全量导入或AOF全量重放啦!因此重启效率也大幅提升
    • 通过配置参数
      aof-use-rdb-preamble
      就可以开启混合式持久化方式,可以通过
      config get aof-use-rdb-preamble
      命令来检查

    混合持久化策略的优缺点

    • 优点:结合了RDB和AOF的优点,使得数据恢复的效率大幅提升
    • 缺点:兼容性不好,redis-4.x新增,虽然最终的文件也是.aof格式的文件,但在4.0之前版本都不识别该aof文件,同时也因为前部分是RDB格式,阅读性很差。

    运维最佳实践

    • 快照是通过fork子进程来执行的,它是一个比较耗时的操作,大块写磁盘会加重磁盘负载。默认持久化不建议只使用RDB的方式。建议使用AOF模式或混合式持久化模式
    • AOF的方式有三种策略,不建议在生产环境中使用
      always
      , 因为
      fysnc
      是一个耗时的IO操作,它会降低Redis的性能。也不建议使用
      no
      策略,因为让操作系统来决定如何同步磁盘,这时间可长可短,不安全,所以建议使用
      everysec
      策略
    • 在集群环境中,不建议使用Redis的主节点都进行持久化操作,而是放到其从节点进行。因为从节点是备份节点,一般没有来自客户端的请求压力,所以它的操作系统资源一般都比较充沛
    • 做完缓存集群的网络和资源监控检查,同时要注意一点的是,要给Redis实例所在的机子预留一部分的内存,用于给fork的子进程使用

    其他问题

    RDB持久化操作时,子进程拷贝父进程的数据副本用于持久化,不会增加内存消耗吗?

    我们知道了解到,Redis的RDB持久化是基于多进程COW机制实现的。我们也简单的讲解了Copy On Write是什么。但是Redis的多进程COW机制也并非就仅仅是复制数据副本来生成数据快照这么简单的,之前说的这么简单,只是为了更容易理解

    那么,那么复杂的情况是怎么个样子呢?

    • RDB持久化操作期间,由子进程在做数据持久化,子进程并不会修改现有的内存数据结构,它只是对内存数据结构进行遍历读取,然后序列化写到磁盘中。然而父进程则不同,因为即使持久化期间,也可能会多个客户端还在不断的向Redis发送读写请求,所以父进程需要不断的对内存数据结构进程修改和更新操作。

    因为子进程生成的数据快照肯定是某个时间节点的,而父进程可能又在时刻改变子进程要扫描的内存数据结构,那怎么办呢?

    • COW机制中的父子进程并非是完全独立的进程,他们之间的关系更像是一个连体婴儿,虽然是两个进程,但是实际在子进程刚刚被fork出来的时候,父子进程实际共享的是同一段内存空间,就相当于有两个头,但却共享一个身体,不过之后还是会慢慢剥离的,前期只是为了尽量共享,节约资源。
    • 此时Redis,就会使用操作系统的COW机制进程数据段页面的分离(数据段是有很多操作系统的页面组合而成)。当父进程对其中一个页面的数据进行修改的时候,会将被共享的数据页面复制一份分离出来,然后对这个复制的页面进行修改。此时子进程想应的页面是没有变化的,依然是子进程产生时那一刻的数据
    • 随着父进程接收的写请求越来越多,要复制出来修改的共享页面也越来越多。所以此时内存就会持续增长,但是由于被修改数据的比例一般占Redis总数据的比例不会太大,所以内存也不会增加原来两倍的情况。当然如果你在持久化期间,全部数据都被修改了个遍,那么两倍内存还是有希望的。

    为什么Redis的AOF机制是先成功命令才再记录日志?

    这个问题的资料的确是有些少,Google, Baidu之后也不了了之。所以我只能引入@Hankoking的其中一个理由来回答这个问题了。

    我个人觉得是有一定的道理的,但还是总感觉仅仅这个理由又似乎有些缺少份量,所以希望有明白大佬,可以解答一下个问题。


    参考资料

  • 内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: