美文网首页
Redis RDB及AOF持久化

Redis RDB及AOF持久化

作者: 心中翼 | 来源:发表于2019-03-29 17:41 被阅读0次

    1、Question1:什么叫持久化?(What)

    持久化是将程序数据在持久状态和瞬时状态间转换的机制。通俗的讲,就是瞬时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)。

    2、Question2:Redis不就是数据库吗,为什么要持久化?(Why)

    Redis确实是一个数据库,但它是基于内存的数据库。
    类似于你用代码写了个变量String a = 123 这个a在程序运行时存入内存中,等程序停止,你还能找到a吗?显然是不行的。
    所以Redis需要有一个持久化的功能,将它存储的数据记到硬盘上。

    3、Question3:Redis如何进行持久化?(How)

    Redis为了能把数据写到硬盘中,它提供了两种持久化功能,分别是RDB (redis database)、AOF (append only file)。下面是对两种方法的说明。

    3.1、RDB持久化是什么?

    RDB持久化是将Redis数据库中的数据快照进行备份,将内存中的数据库状态保存至磁盘,避免数据丢失。


    image.png
    RDB

    3.2、RDB持久化怎么做?

    RDB持久化既可以手动执行,也可以根据服务器配置选项定期执行。

    1、手动执行

    手动执行有两个命令可以选择,save bgsave
    save 命令会阻塞Redis服务器进程,直到RDB文件创建完毕。阻塞期间,服务器不能处理任何命令。这个命令估计没人敢用。

    save命令

    bgsave 命令是非阻塞的,它会创建一个子进程,由子进程负责创建RDB文件,服务器进程继续处理命令请求,这个命令看着比较靠谱。

    bgsave

    很明显,bgsave命令执行后,客户端会收到"Background saving started",并且可以执行命令。bgsave命令执行期间,客户端发送的savebgsave命令会被服务器拒绝。
    用伪代码表示:

    def SAVE():
          # 创建RDB文件
           rdbSave()
    
    def BGSAVE():
          # 创建子进程
         pid = fork() ;
         if pid ==  0 :
             # 子进程负责创建RDB文件
             rdbSave()
             # 完成后向父进程发送信号
             signal_parent()
         elif pid > 0:
             # 父进程继续处理命令请求,并通过轮询等待子进程的信号
             handle_request_and_wait_signal() 
         else:
             # 处理出错情况
              handle_fork_error()
    
    2、服务器配置

    Redis允许用户通过设置服务器配置的save选项(这个不是指save命令),让服务器每隔一段时间自动执行一次bgsave命令。 save选项可以被配置多个,只要有一个条件满足,就会执行bgsave命令。配置文件为redis.conf。在文件中可以找到以下配置:

    save 900 1
    save 300 10
    save 60 10000
    

    该配置表示只要满足以下三个条件中的任意一个,bgsave命令就会被执行:

    • 服务器在900秒之内,对数据库进行了至少1次修改。
    • 服务器在300秒之内,对数据库进行了至少10次修改。
    • 服务器在60秒之内,对数据库进行了至少10000次修改。
      将save选项改为save 60 1,往数据库写入一条数据,可以看到日志会输出以下信息:
      log
    3、RDB文件如何载入?

    将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可,redis在启动时就会自动加载文件数据至内存了。Redis 服务器在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。查看启动日志。


    log
    4、RDB持久化的优势与劣势

    优势:

    • RDB是一个非常紧凑(compact)的文件,它保存了redis 在某个时间点上的数据集。这种文件非常适合用于进行备份和灾难恢复。
    • 生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
    • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

    劣势:

    • RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作(内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑),频繁执行成本过高(影响性能)
    • RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题(版本不兼容)
    • 在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改(数据有丢失)
    5、RDB持久化的原理

    Redis有个服务器状态结构:

    struct redisService{
         //1、记录保存save条件的数组
         struct saveparam *saveparams;
         //2、修改计数器
         long long dirty;
         //3、上一次执行保存的时间
         time_t lastsave;
    }
    struct saveparam{
         //秒数
         time_t seconds;
         //修改数
         int changes;
    };
    

    saveparams属性是一个数组,数组中的每个元素都是一个saveparam结构,当设置save选项为以下条件时:

    save 900 1
    save 300 10
    save 60 10000
    

    saveparams数组是如下结构:

    saveparams
    dirty计数器记录距离上次执行save命令或者bgsave命令后,服务器进行了多少次修改(写入、删除、更新等)
    lastsave属性是一个UNIX时间戳,记录上一次成功执行save或者bgsave 命令的时间。
    通过这两个属性,服务器没执行一次修改,就将dirty计数器加一。成功执行save或者bgsave命令后,将执行时间记录到lastsave属性中。
    服务器有个周期性函数serverCron默认每隔100毫秒就会执行一次。它有一项工作就是检查save选项所保存的条件是否满足,满足的话,就执行一次bgsave命令。
    伪代码表示:
    def serverCron():
          #....
          #遍历所有保存条件
          for saveparam in server.saveparams:
                #计算距离上次保存操作有多少秒
                save_interval = unixtime_now() - server.lastsave ;
                # 如果数据库状态的修改次数超过条件所设置的次数
                # 并且距离上次保存的时间超过条件所设置的时间
                # 那么执行操作
                if server.ditry >= serverparam.changes and 
                    save_interval > server.seconds:
                    BGSAVE()
    #....
    

    3.3、AOF持久化是什么?

    AOF(Append only file) 持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。


    aof

    3.4、AOF持久化怎么做

    将redis.conf配置项appendonly no改为appendonly yes,重启redis即可。

    image.png

    3.5、AOF原理

    AOF持久化分为三个步骤 命令追加(append)、文件写入、文件同步(sync)
    这里有很多人会迷惑,文件写入跟文件同步的区别两个难道不是一个操作吗?

    对用户而言,是一个操作,就是将内容写入文件。但是操作系统为了提高效率,会将写入数据暂时保存在一个内存缓存中,等到缓存区被填满或者到了一定时间时,才将数据写入到磁盘中。所以,如果只是将数据写入文件而不同步,还是存在丢失数据的风险。

    当AOF持久化功能打开后,服务器在执行完一个写命令后,会以协议格式将命令保存至redisServer的aof_buf缓冲区中。

    struct redisServer{
       //aof缓冲区
        sds aof_buf ;
    }
    

    AOF有个配置参数appendfsync是决定什么时候将aof_buf缓冲区的数据写入和保存到AOF文件里面。有三个参数可选:


    image.png
    参数 描述
    always 将aof_buf缓冲区的所有内容写入并同步到AOF文件中。每个事件循环都执行效率最慢,安全性最高。宕机时丢失数据会最少,会丢一个事件循环的数据。
    everysec 默认选项。将aof_buf缓冲区的所有内容写入到AOF文件中,如果距离上次同步AOF文件超过一秒钟,再次同步。宕机时,有可能会丢失一秒钟数据。
    no 这个配置项不是说不保存数据,而是每个事件循环都将aof_buf缓冲区的所有内容写入到AOF文件,是否同步取决于操作系统。速度最快,宕机时丢的数据最多。

    3.5、AOF存在的问题

    1. AOF存在的问题

    AOF不同于RDB持久化,RDB保存的都是有效数据,而AOF保存的是执行命令,随着服务器运行,AOF文件会越来越大,这是它的一个弊端。
    例如,RPUSH list "a" "b" RPUSH list "c" "d"
    RDB只会保存数据list a b c d一条记录,AOF会保存两条命令。

    2. AOF重写,解决AOF文件过大问题

    redis提供一种方式可以解决上面的问题:AOF重写
    就是每隔一段时间重新生成新的AOF文件代替现有的AOF文件。新的AOF文件不需要去对现有的AOF文件进行任何操作,它是通过读取数据库状态实现的。有两个参数会触发AOF重写,当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。一般都设置为3G,64M太小了。

    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb
    

    AOF重新会从数据库中读取键现有的值,然后用一条命令去记录键值对。
    例如,RPUSH list "a" "b" RPUSH list "c" "d"
    现有的AOF是保存了两条命令,AOF重写文件会用一条命令代替上面的两条命令,所以新的AOF文件会比现有的AOF文件占用空间小。

    3. AOF重写问题

    redis将AOF重新程序放到子进程里进行,不至于影响主线程,但随之而来会存在一个问题,子进程在AOF重新期间,新的命令无法进入子线程被写入AOF文件中。

    4. AOF重写缓存区,解决AOF重写过程中无法保存新命令问题

    redis为了解决重写的这个问题,设置了一个AOF重写缓冲区,它会记录AOF重写期间的命令,在子进程完成重写后,将AOF重写缓冲区的命令写入新的AOF文件中,并替换现有的AOF文件。

    相关文章

      网友评论

          本文标题:Redis RDB及AOF持久化

          本文链接:https://www.haomeiwen.com/subject/jcslbqtx.html