欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

并发编程之Redis:Redis数据持久化及故障恢复方案

程序员文章站 2022-04-10 08:20:45
...

并发编程之Redis:Redis数据持久化及故障恢复方案

为什么要做持久化?

  • 原因
    用过Redis的应该都知道,其数据都是保存在内存中,这也是它性能这么好的主要原因;但是这样就会带来一个问题,就是当Redis进程挂了或者机器挂了之后,内存中的数据就没了。那为了保证数据不丢失或者尽量少了丢失,我们希望数据能够落地到磁盘中,来降低故障发生之后数据丢失的可能性;当再起启动服务的时候,通过持久化的磁盘数据,快速恢复到内存中;
  • 持久化的作用
    • 核心作用故障恢复,如果一切都按我们想象的去发展,就可以不存在什么持久化;往往事与愿违,你越不想发生的事情偏偏他就发生了。
    • 数据回滚
      为什么要说数据回滚?既然是人写的代码,不可避免的就可能有Bug,如果上线的一个功能,因为Bug的原因,导致当前redis中缓存的数据错乱或者更加严重的事故;那么我们上线的服务可能会紧急回滚;同时可以借助之前的持久化文件,将Redis中的数据同样也回滚到发布之前的状态;
  • 如何做好一个企业级的持久化?
    并发编程之Redis:Redis数据持久化及故障恢复方案
    上面有说到,持久化的核心作用是为了故障恢复,既然redis可能故障,机器同样也会故障;就算是数据落到磁盘了,同样也可能因为磁盘故障,导致数据丢失;如上图!为了做好一个企业级的持久化方案,我们需要将持久化文件定期同步到云端或者远端的服务器,做好分布式存储,来防止因为机器故障带来的灾难性数据丢失。

Redis提供的持久化方案

Redis官方提供了两种持久化方式,快照(RDB)AOF日志,默认情况下,Redis是开启的RDB,关闭了AOF。

快照(RDB)
  • 什么是快照?
    快照其实就是一次全量备份,其数据是以二进制二进制序列化的形式存储;
  • 快照持久化可能产生的问题?
    Redis是一个单线程程序,此线程需要处理客户端的读写请求,同时还得处理程序内部的逻辑读写;当Redis需要进行持久化的时候,必定就会产生IO操作,IO是比较耗时、耗性能且IO操作又不能使用多路复用;还有个更严重的问题就是,Redis在持久化的时候,数据可能还在发生变化,比如,一个大key,持久化了一半,结果客户端把他给改了,那这会儿该怎么办?是同步,还是不同步?
  • Redis如何解决快照问题?
    并发编程之Redis:Redis数据持久化及故障恢复方案
    Redis使用操作系统的**COW**(Copy on Write)机制来实现快照持久化,持久化触发会调用glibc的函数fork一个子进程,然后持久化的操作全部交由子进程去完成,父进程继续执行来自客户端的请求;如上图,子进程产生的时候,和父进程共享内存中的代码段数据段;linux的机制为了节约内存资源,尽可能的将他们共享,因此,子进程产生的时候,几乎不会带来内存增长;当主进程发生写数据的时候,其实也就是对某一页的数据进行修改,因此会Copy出一份相同的数据,然后对复制出来的那一页进行修改,子进程看到的数据依然还是原来的那份;这样主进程的操作不会受到影响,子教程同样也不用担心数据会发生变化了,当他产生的那一瞬间,数据相当于凝固了,这样他就可以安安心心的进行序列化并持久了。数据持久化的操作很快,就算是这个期间来了大量的写操作,也最多只会增加原来2倍(所有页都被copy了一份)的空间
  • Redis快照持久化的规则(默认开启)
    // save <seconds> <changes>
    // 检查规则为 时间范围生成的数据变化次数
    // 默认redis为以下的检查点;每一分钟有10000个key发生变化,就持久化一次,其他同理
    save 900 1
    save 300 10
    save 60 10000
    
  • 快照文件保存的位置
    // 可以通过conf的dir参数进行配置路径
    dir /var/redis/6379/data/  (根据自己情况选择合适的路径)
    
    并发编程之Redis:Redis数据持久化及故障恢复方案
  • 手动RDB同步的指令
    // 同步持久化
    save
    // 异步持久化
    bgsave
    
    并发编程之Redis:Redis数据持久化及故障恢复方案
  • RDB的优点
    1. Redis会定期生成持久化文件,因此非常适合做上面说道的企业级冷备方案;定期将这个文件复制备份到其他服务器,防止数据丢失
    2. RDB的性能要比AOF的高,因为他对Redis的写操作几乎没有什么影响
    3. RDB文件在数据恢复的时候,相比与AOF要快;因为起存储的是二进制序列化数据,直接加载进来即可
  • RDB的缺点
    1. 相比与AOF,数据丢失的可能性更大,丢失的数据可能更多;因为如果出现故障,从上一次持久化到当前这个时间段的所有数据可能都丢了
    2. fork子进程的时候,如果数据量大的时候,可能会导致客户端毫秒级或者秒级别的服务暂停
AOF
  • 什么是AOF?
    通俗一点说,其实就是所有写操作的一份日志记录文件;当需要对数据进行恢复的时候,就把整份日志进行回放,即可完成整个的数据恢复;当Redis收到客户端的写操作之后,经过参数校验,逻辑处理,没有问题之后会将指令写到AOF日志;也就是说,整个过程是先执行指令,再写日志;
  • AOF重写(rewrite)
    AOF是将所有的写操作记录日志,那么随着时间的推移,这个日志文件会变得越来越大,同时也会很臃肿;因为里面可能记录了大量不起作用的日志,比如,一个key a,执行了一下三次操作,set a 1,set a 2,del a,那么在AOF中,其实保存了3条日志,当要做数据回放的时候,这三个动作都得做一遍,但是三个操作其实是没有任何意义的操作,因为第三步已经将其删掉了;因此Redis会定期对AOF日志进行瘦身;原理就是开启了一个子线程,将Redis中的数据转换为操作指令,在序列化成AOF文件,序列化完毕之后,会将操作期间产生的写操作追加到新的AOF中去。
    // 手动rewrite的指令
    bgrewriteaof
    
    • rewrite策略参数
      // 可以在redis.conf文件中通过以下参数配置rewrite策略
      // 表示当前的AOF文件相比与上次rewrite之后的大小增加了百分之多少,默认是100%
      // 如上一次rewrite后是100M,当大小超过200M之后就会触发rewrite
      auto-aof-rewrite-percentage 100
      // 上面的参数触发之后,还需要和这个参数进行比较
      // 比如上一次rewrite之后AOF文件之后10M,当达到20M后上面条件会触发
      // 但是他总的大小依然小于最小值64M 所以不会触发rewrite
      auto-aof-rewrite-min-size 64mb
      
  • fsync
    既然是每一次操作都将数据刷到日志文件,那么是不是就意味着数据不会丢失呢?不然!同样也可能会丢失1s的数据,当进行日志写操作的时候,其实是写到了OS Cache中,然后由操作系统定期将Cache中的数据持久化的磁盘;Linux的glibc提供了fsync(int fd)函数来强制将指定文件刷到磁盘,Redis默认是采用的1秒刷一次的策略,因此,就可能造成1s数据丢失的情况;
  • 能够保证数据不丢失
    既然是1s刷新一次,能否每次都刷?是可以的,但是我们使用Redis的目的就是为了提高性能,实现高并发,如果采用了每次都刷盘的方式,将大大降低Redis的性能,可能得不偿失。具体的刷新策略可以通过appendfsync方式设置
    appendfsync everysec
    // 该参数有三个值
    // 1. always 就是上面所说的,每次写操作刷一次磁盘,他可能保证数据不丢失,但是性能很差
    // 2. everysec(默认) Redis每一秒刷一次磁盘
    // 3. no 标识Redis不执行刷磁盘的操作,只是将数据写到OS Cache中,具体什么时候刷磁盘由操作系统决定
    
  • AOF的优点
    1. 可以保证数据更少丢失或者不丢失的风险
    2. AOF以append only的模式进行写入,所以不存在寻址,性能很高;以追加的形式,文件不会破损,就算出现破损,也只是出现在文件尾部
    3. AOF rewrite对客户端的操作影响更小
    4. AOF以可读性很高的方式进行存储,因此可以用来做误操作的数据恢复;比如说因为误操作执行了flushdb或者flushall将数据删除了,那么在下一次rewrite之前,我们可以采用停止redis服务,将AOF中flush的执行删除,再回放的方式将数据恢复
  • AOF的缺点
    1. 因为写操作多一次文件操作,因此开启了AOF的QPS相比于要低于RDB的方式
    2. 相同数据,AOF的持久化文件要比RDB的大
    3. 数据恢复的方式相比与RDB来说脆弱一些,容易出些Bug,导致恢复出来的数据和之前的数据不一致
    4. 不适合做数据冷备

企业级的持久化如何选择?

基于RDB和AOF各自优缺点,企业级的持久化策略不能仅仅使用RDB,也不能单纯使用AOF;因为RDB可能造成更多的数据丢失,AOF不太适合做冷备、数据恢复较慢、容易出Bug,因此,常用的策略就是两种方案共同使用;既能保证数据尽可能少的丢失,同样又有一个非常好的冷备方案;在特定业务场景或者特定业务,可以考虑只选用其中的一种,这个就属于特例了。

  • 新安装的Redis开启AOF(只针对新装)
    // redis.conf(/etc/redis/6379.conf)中的appendonly参数设置为yes
    // 注意,这里只是针对新安装
    appendonly yes
    
  • 已经存在数据的Redis开启AOF
    特别提醒特别提醒特别提醒!不要和新装一样直接修改配置文件开启,数据会丢的数据会丢的数据会丢的!)
    • 直接修改数据丢失的问题分析
      当Redis通过RDB保存数据之后,数据会持久化到dump.rdb文件中;当我们通过配置文件(6379.conf)直接开启AOF,Redis在启动的时候,会自动创建一个为空的appendonly.aof,当加载持久化数据到内存的时候,如果持久化采用的RDB+AOF的当时,那么就默认使用AOF进行数据恢复,但是当前appendonly.aof文件是新创建的,因此加载大内存的数据就是一个空库了。知道问题之后,就可以通过一下步骤去开启;
    • 第一步,appendonly no的情况下启动redis,保证数据能通过dump.rdb文件载入内存
    • 第二步,通过redis-cli 连接redis服务
    • 第三步,通过设置临时参数(重启会失效)开启AOF,此时,Redis会基于最新的内存数据生成aof文件
      config set appendonly yes
      
    • 第四步,确认基于最新的Redis数据,生成了appendonly.aof文件
    • 第五步,redis config配置文件中开启appendonly
      appendonly yes
      
    • 第六步,优雅关闭,重启redis
      redis-cli shutdown
      cd /etc/init.d
      ./redis_6379 start
      
      并发编程之Redis:Redis数据持久化及故障恢复方案

Redis冷备

这里的冷备是基于RDB的dump.rdb文件进行,通过脚本,定时将dump.rdb拷贝到其他机器

  • 小时级备份脚本
    // 创建两个目录
    // 用于保存缓存文件
    mkdir -p /var/redis/6379/cache
    // 用于保存脚本
    mkdir -p /var/redis/6379/copy
    cd /var/redis/6379/copy
    
    创建按小时备份的脚本 vim redis_rdb_copy_hourly.sh
    #!/bin/sh
    
    # 生成一个按小时的变量 作为备份的文件名
    cur_date=`date +%Y%m%d%k`
    
    rm -rf /var/redis/6379/cache/$cur_date
    mkdir -p /var/redis/6379/cache/$cur_date
    cp /var/redis/6379/data/dump.rdb /var/redis/6379/cache/$cur_date
    
    # 删除48小时前的备份
    del_date=`date -d -48hour +%Y%m%d%k`
    rm -rf /var/redis/6379/cache/$del_date
    
    配置定时任务,按小时执行
    crontab -e
    
    // 添加以下配置
    0 * * * * sh /var/redis/6379/copy/redis_rdb_copy_hourly.sh
    
    手动执行查看效果
    sh /var/redis/6379/copy/redis_rdb_copy_hourly.sh
    cd /var/redis/6379/cache/
    ll
    // 发现已经成功执行并备份了
    
    并发编程之Redis:Redis数据持久化及故障恢复方案

模拟容灾演练

基于纯RDB的容灾
  • 测试未持久化数据丢失的情况
    // 默认情况下 持久化的第一个检查点在5分钟,因此我们保存数据之后通过kill -9 暴力杀死进程
    redis-cli
    set a 1
    set b 2
    get a
    get b
    
    // shutdown会自动做数据持久化并优雅关闭
    redis-cli shutdown
    
    并发编程之Redis:Redis数据持久化及故障恢复方案
  • 测试持久化之后的数据恢复
    // 修改redis配置文件
    vim /etc/redis/6379.conf
    // 配置一个5秒有1条数据发送变化就做持久化的策略
    save 5 1
    
    // 重启redis
    redis-cli shutdown
    cd /etc/init.d
    ./redis_6379 start
    
    //做上面相关的测试
    
    并发编程之Redis:Redis数据持久化及故障恢复方案
基于AOF+RDB的容灾演练

当我们开启了AOF+RDB,在数据恢复的时候,默认使用的AOF进行

// 当我们执行插入一个c=3的key
set c 3
// 查看aof文件,发现已经保存了相关的日志

并发编程之Redis:Redis数据持久化及故障恢复方案
再次启动,发现数据依然存在
并发编程之Redis:Redis数据持久化及故障恢复方案

基于AOF+RDB数据回滚恢复

回滚数据的操作和存在数据中途开启AOF的操作几乎相关

  1. 第一步,将redis服务关闭
redis-cli shutdown
  1. 第二步,关闭AOF 将redis.conf
appendonly no
  1. 第三步,找到要恢复的dump.rdb冷备文件 将起拷贝到数据保存目录
  2. 第四步,备份最新aof配置文件(防止后续的操作失败,还可以基于它恢复)
  3. 第五步,启动redis服务并通过redis-cli连接服务
  4. 第六步,通过设置临时参数开启aof
config set appendonly yes
// 确保基于最新的redis数据成功了appendonly.aof文件
  1. 第七步,修改配置文件,开启AOF
appendonly yes
  1. 重启redis
redis-cli
cd /etc/init.d
./redis_6379 start
误删数据的容灾恢复
  • 第一步,模拟删除数据
    FLUSHDB
    
  • 第二步,防止rewrite,第一时间关闭Redis
    redis-cli shutdown
    
  • 第三步,查看aof日志文件
    // 保存持久化文件的目录
    cd /var/redis/6379/data
    vim appendonly.aof
    // 删掉最后的 FLUSHDB指令
    
    并发编程之Redis:Redis数据持久化及故障恢复方案
  • 第四步,防止文件有问题,修复一下
    redis-check-aof appendonly.aof
    
    并发编程之Redis:Redis数据持久化及故障恢复方案
  • 重启redis
    redis-cli
    get a
    // 发现数据已经恢复
    
    并发编程之Redis:Redis数据持久化及故障恢复方案

码字不易,感谢您的点赞!关注!评论!!!

并发编程之Redis:Redis数据持久化及故障恢复方案