• Redis(1.4)Redis的持久化


    Redis持久化

    【1】概念

      Redis所有的数据存储在内存中,为了保证重启后,redis数据不丢失,需要把redis数据保存在磁盘中。

    【2】持久化使用方式策略

      (1)RDB 方式:默认支持,不需要配置

          在指定的时间间隔内,将redis 内存中的数据集快照写入到磁盘当中去,比如每隔30S写入一次。

      (2)AOF方式

          以日志的形式记录服务器所处理的的每一个操作,在服务器启动之初,会读取该文件来重新构建redis数据库数据,以此来保证重启后数据库的数据是完整的。

      (3)不持久化

          单纯做缓存用

      (4)RDB+AOF 同时使用

    【3】两种核心方法

      (1)RDB

        优势

          (1.1)整个redis数据库只包含1个文件

          (1.2)对于灾难恢复而言,可以通过拷贝压缩等传输到异地机

          (1.3)性能最大化:在开始持久化的时候,只需要分一些进程,接下来就可以由子进程完成持计划的工作。

              可以避免服务器进程进行持久化操作。数据量大的时候,恢复速度很快。

        劣势

          (1.1)对于高可用性和数据安全性而言:会丢失一个间隔内的数据,比如30秒一次持久化,但28秒的时候宕机了,那么就会丢自上一个备份后的28秒的所有操作。

          (1.2)当数据集非常大的时候,需要服务器停止一点时间甚至超过1s

        

        基本操作与配置:

          (1.1)默认的存盘配置如下(大概在文档15%的位置)

          

             如英文描述一样。

               save num1_second num2_key   每num1秒中,有num2个key有修改,则存盘

              这里的默认就是每900/300/60 秒 有 1/10/10000 个 key 被修改,就存盘。

          (1.2)默认redis数据库文件名称与存放路径(大概在文档15%的位置)

                

               上面是文件名,下面是上面文件所在的路径,  ./的意思就是,和当前打开的配置文件同一路径。

                

        

      (2)AOF

        优势:

          (2.1)3种同步策略

              《1》每秒同步:异步持计划,效率高,会丢失1S内数据

              《2》每修改同步:同步持久化,效率低,不会丢数据

              《3》不同步

          (2.2)日志文件

              《1》文件写入形式:使用的是append追加方式,如果宕机不会被破坏已经写入的数据。

              《2》宕机恢复:如果一个命令写到一般就宕机了,可以用 reids-check-aof 工具来解决数据一致性问题。

              《3》重写机制:如果日志文件过大,redis可以自动启用重写重写机制.

                      把旧的比如 del key1 、hdel key2 等无效记录给清理掉,set key1 、set key2等操作也可以合并成 mset,保留能重做出现在数据的最精简记录。

                         同时redis还会创建一个新的文件来记录此期间哪些修改命令被执行了。因此在进行重写切换的时候,可以更好的保证数据那权限。

                参数:     

    no-appendfsync-on-rewrite no   #是否在重写的时候 停止追加命令,no为不停止,依然会新建一个文件来记录重写期间的命令
    auto-aof-rewrite-min-size 64mb  #重写文件的最小文件大小,这里是重写的文件必须要大于64M以上才开始重写
    auto-aof-rewrite-percentage 100 #重写文件的百分比阀值,这里写的是文件增长100%大小 才重写

              《4》可读性强:日志文件记录了很详细的修改操作记录和数据,可以用此文件重建数据库。

        劣势

          (2.1)文件大小:AOF比RDB文件大许多

          (2.2)执行效率:每次操作都需要记录,比起RDB每过一段时间批量持久化记录,要慢不少。

          (2.3)需要额外配置:默认是关闭的,如果要启用则要把下图中第1行改成 yes,开启后下面就是文件名。

        基本操作与配置:   

         (2.1)开启:

            最后面的3行是同步策略,always 是修改后就立马同步,everysec 是每一秒,另外一个是不同步

             

           (2.2)修改配置:进行配置放开测试,修改如下图(要想生效,需要重启redis)

             

           (2.3)重启redis后查看文件:

            就可以发现文件了(默认路径是和conf文件在同一目录下)

            

           (2.4)模拟通过aof文件恢复

            《1》删掉所有key:flushall      -- 可以刷掉所有key

               《2》关掉redis  :./redis-cli shutdown

            《3》编辑aof文件:vim ./appendonly.aof

              对比一下我们的输入命令和aof文件记录的命令;

                  

              把 flushall 在aof 文件里删掉,就可以用 aof 文件来重做恢复我们的数据库数据。(redis 会自动加载 aof 文件的数据操作)

             《4》启动redis -》连接 redis =》查看key是否存在

                

              《5》修复 aof文件(开启错误信息日志文件)

                故障模拟操作:当我没有关系redis 服务端的时候,我去修改了 aof文件

                    重新启动,不报任何错误,但用 ps -ef|grep redis 发现进程没有起来。

                    如何获取报错信息呢?修改 redis.conf 文件,找到logfile 字样,这里直接写名字的话默认路径是在配置文件相同目录下。也可以写URL

                      这里原本值是"",我改成了如下图。

                      

                    

                    再次启动服务,查看错误日志文件。报错信息如下

                    

                 需要我们修复 aof 文件之后,达成了一致性,才可以启动成功。

                  ./redis-check-aof --fix ./appendonly.aof

                 修复后然后再次启动,核验启动成功

                  

              

     【4】核心原理总结

    我们熟知redis是内存数据库,它将自己的数据存储在内存里面,如果如图redis进程退出或突然宕机,数据就会全部丢失,因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的持久化机制。

    Redis 的持久化机制有三种,第一种是快照(RDB),第二种是 AOF 日志,第三种是混合持久化。快照是一次全量备份,AOF 日志是连续的增量备份。

    快照是内存数据的二进制序列化形式,在存储上非常紧凑,而 AOF 日志记录的是内存数据修改的指令记录文本。混合持久化综合了上面两种的优点。

    【4.1】RDB原理  

      RDB持久化是把当前进程数据生成快照保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发

    触发机制:

      1.save命令:阻塞当前redis服务器,直RDB过程完成为止。(如果内存比较大会造成redis长时间阻塞,这样显然不是我们想要的。线上禁止使用)
      2.bgsave命令:redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般很短(和实例数据大小有关系)


    除了执行命令手动触发之外,redis内部还存在自动触发RDB的持久化机制,如下

      1.使用save相关配置,如“save m n” 表示m秒内数据集存在n次修改时(可以配置多组条件,其中一个达标就触发),自动触发bgsave
      2.如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点(具体复制细节见下一篇)
      3.执行debug reload命令重写加载redis时,
      4.默认情况下执行shutdown命令时,如果没有开启aof持久化则自动执行bgsave
    bgsvae运作流程如下

       

      1.执行bgsave命令,redis父进程判断是否存在正在执行的子进程,如RDB/AOF子进程,如果存在bgsave命令直接返回(生成RDB、AOF要浪费大量磁盘io资源,如果开启aof,磁盘io有可能成为redis的瓶颈)

      2.父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞,通过info stats 命令查看latest_fork_usec选项,可以获取最近一个fork操作的耗时(单位ms)(具体阻塞时间和内存大小有关)

         父进程fork完成后,bgsave命令返回“Background saving started”信息并不在阻塞父进程,子进程创建RDB文件,根据父进程内存生成的临时快照文件,完成后对原有的文件进行院子替换。执行lastsave命令可以获取最后一次生成RDB的时间

         子进程发送信号给父进程表示完成,父进程更新统计信息

    至此RDB的生成过程大概讲完了,我们知道Redis为了不阻塞主线程,调用 glibc 的函数fork产生一个子进程来生成RDB备份文件。

    试想一个问题,如果一个大型的 hash 字典正在持久化,结果一个请求过来把它给删掉了,还没持久化完呢,这尼玛要怎么搞??

    Copy On Write

      Redis 使用操作系统的多进程 COW(Copy On Write) 机制来实现快照持久化。子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段.这是 Linux 操作系统的机制,为了节约内存资源,所以尽可能让它们共享起来。在进程分离的一瞬间,内存的增长几乎没有明显变化。子进程做数据持久化,它不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是父进程不一样,它必须持续服务客户端请求,然后对内存数据结构进行不间断的修改。

    这个时候就会使用操作系统的 COW 机制来进行数据段页面的分离。数据段是由很多操作系统的页面组合而成(一页4k),当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据。

    子进程因为数据没有变化,它能看到的内存里的数据在进程产生的一瞬间就凝固了,再也不会改变,这也是为什么 Redis 的持久化叫「快照」的原因。接下来子进程就可以非常安心的遍历数据了进行序列化写磁盘了。

    RDB的载入

      和使用save或者bgsave命令不同,RDB的载入是在服务器启动的时候自动执行的,所以Redis并没有专门用于载入RDB的文件命令。值得一提的是:

    如果服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据库
    数据库在主从复制时候会触发RDB加载(下一篇细聊)
    RDB文件的处理

      保存:RDB文件保存在dir配置的指定目录下,文件名通过dbfilename配置指定。可以通过执行 config set dir{new Dir} 和 config set dbfilename{newFileName} 运行期动态执行。

      压缩:redis 默认采用LZF算法对生成的RDB文件做压缩处理,压缩后的文件远小于内存大小,默认开启,可以通过参数config set rdbcompression{yes|no}动态修改

    RDB的优缺点

    优点:

      RDB是一个紧凑压缩的二进制文件,某个时间点的上的快照。适合全量复制
      redis加载RDB恢复数据远快于AOF的方式
      如果Redis加载损坏的RDB文件时拒绝启动,并打印如下日志:# Short read or OOM loading DB. Unrecoverable error, aborting now.    这时可以使用Redis提供的redis-check-dump工具检测RDB文件并获取对应的错误报告。
    缺点:

      RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高。
      RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题。
      针对RDB不适合实时持久化的问题,Redis提供了AOF持久化方式来解决。

    【4.2】AOF原理

    AOF的主要作用是解决了数据持久化的实时性,AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录。

      Redis 会在收到客户端修改指令后,进行参数校验进行逻辑处理后,如果没问题,就立即将该指令文本存储到 AOF 日志中,也就是先执行指令才将日志存盘。

      这点不同于mysql、hbase等存储引擎,它们都是先存储日志再做逻辑处理。(所以单机redis不能做到一条数据也不丢失)

    使用AOF

      开启AOF功能需要设置配置:appendonly yes,默认不开启。AOF文件名通过appendfilename配置设置,默认文件名是appendonly.aof。

      保存路径同RDB持久化方式一致,通过dir配置指定。AOF的工作流程操作:命令写入(append)、文件同步(sync)、文件重写(rewrite)、重启加载(load)。

    命令写入and文件同步

      服务器在执行完一个写命令后(如 set k v,lpush k v 等),会把写入命令会追加到aof_buf(缓冲区)中。后续防止丢失aof_buf中的数据,在调用linux的glibc提供的fsync函数将aof_buf中的数据强制刷新到磁盘。

      AOF为什么把命令追加到aof_buf中?Redis使用单线程响应命令,如果每次写AOF文件命令都直接追加到硬盘(调用fsync),那么性能完全取决于当前硬盘负载。

      先写入缓冲区aof_buf中,还有另一个好处,Redis可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。

    Redis提供了多种AOF缓冲区同步文件策略,由参数appendfsync控制,不同值的含义如下

      always: 命令写入aof_buf后调用系统fsync操作同步到aof文件,fsync完成后线程返回(性能最差,完全取决于磁盘速度。即便如此redis也不能保证一条数据也不丢)
      everysec: 命令写入aof_buf后调用系统write操作,write完成后线程返回。fsync同步文件操作由专门的线程每秒调用一次(默认配置。理论上会丢失1s的数据。(严格来说丢弃1s的数据不准确,下文有讲))
      no:命令写入aof_bug后调用系统的write操作,不对AOF文件做fsync同步,同步硬盘操作由操作系统负责,通常同步周期30s

    系统调用write和fsync说明:

      write :Linux在内核提供页缓冲区用来提高硬盘IO性能。write操作在写入系统缓冲区后直接返回。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
      fsync : 将指定文件的内容强制从内核缓存刷到磁盘

    AOF重写机制

    我们知道AOF持久化是通过保存被执行的写命令来实现的,随着命令不断写入AOF,文件会越来越大。

    为了解决这个问题,Redis引入AOF重写机制压缩文件体积。AOF文件重写是把Redis进程内的数据转化为写命令同步到新AOF

    重写后为什么变小?

    已经过期的数据不再写入文件。
      旧的AOF文件含有无效命令,如set key1、del key1 。重写使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令。

      多条写命令可以合并为一个,如:lpush list a、lpush list b、lpush list c可以转化为:lpush list a b c。

      为了防止单条命令过大造成客户端缓冲区溢出,对于list、set、hash、zset等类型操作,以64个元素为界拆分为多条

      (其实这里直接遍历的是当前内存数据,如直接把一个大个的list的生成一个个lpush list a1 a2 ..a60 ,步子大了容易扯着蛋,所以默认以64个元素为一组)

    更小的AOF文件可以被Redis更快的加载
    AOF重写触发条件

      手动触发:直接调用bgrewriteaof命令。

      自动触发:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机。

      auto-aof-rewrite-min-size:表示运行AOF重写时文件最小体积,默认为64MB。
      auto-aof-rewrite-percentage(设为x):代表当前AOF文件空间(aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的百分比(如auto-aof-rewrite-percentage 100 当为两倍大小是重写)。
      自动触发条件:aof_current_size > auto-aof-rewrite-min-size && (x+1)*aof_base_size

      其中aof_current_size和aof_base_size可以在info Persistence统计信息中查看。

    AOF重写Redis做了哪些事情呢?

      AOF重写功能作为一个辅助功能,redis肯定不希望阻塞主进程的执行,所以redis把AOF重写放到一个子进程去执行。

      上文讲到frok运用cow(写时复制技术)技术,所以子进程只能看到fork那一瞬间产生的镜像数据。

      为了解决这一个问题redis设置了一个 AOF重写缓存区(aof_rewrite_bug) 用来存储AOF重写期间产生的命令,等子进程重写完成后通知父进程,父进程把重写缓存区的数据追加到新的AOF文件

      (注:这里值得注意,AOF重写期间如果有大量的写入,父进程在把aof_rewrite_buf写到新的aof文件时会造成大量的写盘操作,会造成性能的下降,redis 4.0以后增加管道机制来优化这里(把aof_rewrite_buf追加工作交给子进程去做),感兴趣的同学可以自行查阅)

    所以说在AOF重写期间主进程做了下面几件事

      1.执行客户端发来的命令
      2.将执行后的写命令追加到AOF缓冲区
      3.将执行后的写明了追加到AOF重写缓冲区
    值得注意的是,redis每次重写写入磁盘的大小由aof-rewrite-incremental-fsync控制,默认为32MB,防止单次刷盘数据过多造成硬盘阻塞

    过程如图:

       

    我们知道AOF重写期间会消耗大量磁盘IO,可以开启配置no-appendfsync-on-rewrite yes,表示在AOF重写期间不做fsync操作,默认为no。但是如此一来某些情况下会丢掉重写期间的所有数据。慎重啊,铁子

    重启加载

      讲RDB时提到,如果开启AOF优先加载AOF文件,否则执行RDB文件

    AOF追加阻塞

      当开启AOF持久化时,默认以及常用的同步硬盘策略是everysec(每s一刷),对于这种方式,Redis使用另一个线程每s执行fsync同步磁盘。试想一个问题,假设硬盘资源繁忙,fsync刷盘缓慢,主线程该如何做?

      主线程写入AOF缓冲区后会对比上次AOF同步时间

        如果距上次同步成功时间在2S内,主线程直接返回
        如果距上次同步成功时间超过2s,主线程阻塞,直到同步操作完成
    发现两个问题:

      everysec配置最多可能丢失2s数据,不是1s
      如果系统fsync慢会阻塞主线程
      RDB-AOF混合持久化(redis 4.0+提供)

    细细想来aofrewrite时也是先写一份全量数据到新AOF文件中再追加增量只不过全量数据是以redis命令的格式写入。

    那么是否可以先以RDB格式写入全量数据再追加增量日志呢这样既可以提高aofrewrite和恢复速度也可以减少文件大小还可以保证数据的完毕性整合RDB和AOF的优点那么现在4.0实现了这一特性——RDB-AOF混合持久化。

    【4.3】RDB-AOF混合持久化

    综上所述RDB-AOF混合持久化体现在aofrewrite时,即在AOF重写时把frok的那个镜像写成RDB,后续AOF重写缓冲里的数据继续追加到该文件中。

    配置为 aof-use-rdb-preamble no  #默认关闭,yes 打开

    写在最后:
    小结:

    redis提供两种持久化方式:RDB和aof


    RDB使用一次性生成内存快照,产生的文件为二进制序列,非常紧凑,因此加载更快。但是由于其为快照,所以不能做到实时持久化,一般用于冷备和复制传输
    save命令会阻塞主线程不建议使用,bgsave通过fork创建子进程生成RDB避免阻塞
    AOF通过追加写命令到文件实现持久化,因为需要不断追加写命令,所以AOF需要定期执行重写来降低文件体积
    如果写命令直接写入磁盘势必会造成性能过低,所以redis提供了一个AOF缓冲区。写命令写入到AOF缓冲区后续在调用fsync异步刷盘
    AOF子进程执行期间使用copy-on-write机制和父进程共享内存,但是该技术只能看到fork那一瞬间内存的快照,所以需要一个AOF重写缓冲区,保存新的写入命令避免数据丢失
    持久化阻塞主线程的场景有:fork阻塞和AOF追加阻塞,fork阻塞时间跟内存和系统有关,AOF追加阻塞说明磁盘资源紧张

    【4.4】文章涉及到的命令及配置:

    命令 :手动备份RDB:save(阻塞) 和 bgsave(fork子进程)
    配置: save m n 表示m秒内数据集存在n次修改时(可以配置多组条件,其中一个达标就触发),自动触发bgsave
    命令 :debug reload  重新加载redis
    日志文件名配置 : 文件保存在dir配置的指定目录下,文件名通过dbfilename配置指定。可以通过执行 config set dir{new Dir} 和 config set dbfilename{newFileName} 运行期动态执行。
    开启aof: appendonly yes
     appendfsync  always|everysec|no : AOF缓冲区同步磁盘,always:总是同步,everysec:1s刷新,no:交给系统(大概30s)
    命令 : bgrewriteaof :手动重写AOF
    配置:auto-aof-rewrite-min-size:表示运行AOF重写时文件最小体积,默认为64MB。
    配置:auto-aof-rewrite-percentage:代表当前AOF文件空间(aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的百分比(如auto-aof-rewrite-percentage 100 当为两倍大小是重写)。
    配置:aof-rewrite-incremental-fsync:AOF每次重写一次刷盘大小,默认为32MB
    配置:no-appendfsync-on-rewrite :AOF重写期间是否刷新AOF缓冲区,默认为no(刷新)

    【4】转自CSDN,原文链接:https://blog.csdn.net/qq_31387317/article/details/95315166

              

  • 相关阅读:
    ==和===
    Println、Printf、Sprintf区别
    BurpSuite代理https
    scp
    Tomcat Ajp(CVE-2020-1938)
    Chrome-HackBar破解
    crontab
    Sql注入之postgresql
    Sql注入之oracle
    LeetCode简单题(一)
  • 原文地址:https://www.cnblogs.com/gered/p/11686973.html
Copyright © 2020-2023  润新知