详解Redis持久化(持久化高危漏洞利用与多种对抗方案、RDB、AOF、同步手动持久化、异步手动非阻塞持久化、备份检测、备份修复、压缩原理、双备份模式、纯缓存模式)

谨防持久化+未授权访问漏洞入侵服务器

CVE编号找不到,CNVD有一个:CNVD-2015-07557(国家信息安全漏洞共享平台漏洞编号)。
这是我之前写过的文章,漏洞成因、影响范围、POC与对抗方案有详解:
谨防利用Redis未授权访问漏洞入侵服务器

RDB(Redis Database、全量保存,默认方式)

  • 极简概括:通过符合单位时间数据被修改的量作为触发,或手动触发,把某一时刻的全部数据写到磁盘。
  • 现象:编译安装完Redis后启动,默认会在根目录(这默认配置地址得吐槽)下生成一个dump.rdb文件。
  • 会被自动触发的情形:
    • 配置设置。
    • 关闭Redis服务。
    • Flashall命令。
  • 相关配置:从6.2.0开始有差异:
6.2.0之前
save 900 1        15分钟至少有1个值被改动,这是避免备份数据与内存数据长期不一致的兜底策略。
save 300 10       5分钟至少有10个值被改动,中庸策略。
save 60 10000     1分钟至少有1万个值被改动,避免备份数据与内存数据短时间大量不一致的策略。

6.2.0之后,这意味着某些场景,需要改变配置。
save 3600 1
save 300 100
save 60 10000

这种东西怎么配,若数据要紧,时间就配置的少一点,若要求性能,对数据丢失有容忍度,那就按照默认配置,或加大时间配置。


若要修改rdb文件存放位置和名称
dir 自定义路径
dbfilename 自定义文件名.rdb


其它配置:
stop-writes-on-bgsave-error yes
用于在BGSAVE操作出现错误时Redis的行为。当这个配置选项被设置为yes时,如果后台保存操作BGSAVE失败,Redis将停止接受写操作,以防止数据丢失。这样做可以确保数据的完整性,因为在无法完成后台保存操作时,写入新数据可能会导致数据不一致或丢失。
当stop-writes-on-bgsave-error被设置为no时,Redis在后台保存操作失败时仍然会继续接受写操作。这可能会导致数据不一致或丢失,但也可以确保写操作的连续性。

rdbcompression yes
指定Redis在执行RDB时是否启用LZF算法压缩。

rdbchecksum yes
用于指定在执行RDB(Redis Database)持久化时是否对数据进行校验和检查,会有10%的性能消耗。

rdb-del-sync-files no
在没有持久性的实例中删除复制使用的RDB文件。
  • redis/bin/redis-check-rdb,可检测rdb文件是否存坏,并修复。

RDB同步持久化与异步非阻塞持久化

  • save(不推荐用):执行期间会阻塞其它命令(意味着临时停止服务)直到执行完毕,不适合大量请求场景,由于是直接执行,性能比bgsave略高。
  • bgsave:会fork一个子进程去处理备份,异步执行期间不会阻塞其它命令,但是若数据资源相当庞大,仍旧会拉低Redis的性能。
  • lastsave:使用lastsave命令,可以查看最后一次执行RDB的时间,对于Redis运维还是有帮助的。
  • 注意:在持久化时,需要考虑数据量的大小,与磁盘大小,避免内存或者磁盘不足让服务夯住的情况发生。
  • fork: 在Unix/Linux系统中用来创建一个新的子进程的操作。在调用fork() 后,操作系统会复制当前进程的所有内容(包括内存数据、堆栈等)到新的子进程中,从而创建一个完全独立的子进程,而子进程拥有独立的内存空间和进程 ID。

RDB恢复数据

  • 注意:由于Redis RDB是全量备份,所以不会像MySQL binlog那种,可以恢复部分数据。也不是像MySQL redo log那种接近实时的备份,RDB备份有间隙,所以就算通过文件恢复,也可能丢失部分数据。
  • 如果误删了数据:其它节点(如MySQL)又没有备份,可以利用这个间隙,立即把rdb文件备份,防止触发redis同步功能覆盖原数据,把之前持久化的数据整没了,然后停止Redis(一定要在Redis停止之前备份,利用Redis命令停止Redis也会触发一次RDB持久化,利用操作系统命令干掉进程则不会),接着把持久化名字改过来,随后启动Redis。
  • 默认处理方式:Redis在启动时会读取配置,所以RDB文件不需要手动导入,Redis会直接读取里面的数据。
  • 优点:由于RDB文件具有紧凑的单一文件,所以可以方便的被异地备份,常用的容灾手段。

禁用RDB

修改配置文件:清除未被注释的save项,改为一个save ""

AOF(Append Only File、追加写,增量保存)

  • 极简概括:和RDB是并列关系,将Redis服务器执行的写命令追加到一个文件的末尾,由于备份和执行不是实时的原子性操作,所以仍旧有丢失的可能(Redis存在重点是快速用于组件间通信,而不是持久化存储)。
  • 误区:这里的only单词很多余,有only不代表RDB模式不能被同时启用。
  • 存在意义:
    1. 毕竟是主流的数据库,所以要在持久化上利用多备份机制做到位。
    2. AOF弥补了RDB备份时间间隙(最后一次备份~当前时间未保存)的问题。
    3. AOF弥补了RDB备份时产生大量磁盘IO的情况发生。
  • 配置
找到redis.conf
appendonly no 改为yes

配置持久化模式
appendfsync everysec/always/no
always:每次写操作,都进行一次追加,最安全但性能最差。
everysec:默认值,每秒进行一次追加,最多丢失一秒钟的数据。
no:让操作系统来处理数据同步,由操作系统自行决定何时将数据刷新到磁盘。
三者从上到下性能依次提升,高可用依次降低。

配置保存路径,Redis7新配置,若低版本没有,就和RDB目录一个位置
appenddirname 'appendonlydir'

配置文件名称
appendfilename "appendonly.aof"

Redis7针对aof文件做了拆分
appendonly.aof.base:基本文件,在压缩文件时,会将压缩后的最小指令集保留在此当中。
appendonly.aof.incr:增量文件,该文件存储对基本AOF文件进行的增量更改。
appendonly.aof.manifest:清单文件,该文件可能与管理和跟踪AOF文件的状态或元数据。


备份压缩相关配置,下文有讲
上次重写之后的增长百分比,这里默认为100
auto-aof-rewrite-percentage 100

表示aof文件超过64MB。
auto-aof-rewrite-min-size 64mb


杂项配置
no-appendfsync-on-rewrite no
这个配置指示Redis如何将数据写入磁盘,设置为no表示Redis在重写时不会改变其关于appendfsync设置的行为。

AOF恢复数据

  • 常规情况下:aof备份文件恢复数据是自动的,不需要手动操作。
  • 若手动备份文件的方式恢复:若对aof文件做了更名用于多文件备份,恢复时需要改为配置文件中的名称,然后重启,即可恢复aof文件中的数据。
  • 注意:由于flushall也是写操作,所以执行它有也会同步aof文件,但是由于aof是增量备份,所以在aof文件中删除flushall字眼,然后重启,就能恢复数据。

AOF破损修复与检测

redis-check-aof 是专门用于检测与修复aof文件的工具,在redis-cli同级目录。

检测,看到AOF is valid说明文件无问题:
./redis-check-aof ../appendonly.aof 
AOF analyzed: size=131, ok_up_to=131, diff=0
AOF is valid


但是由于断电,进程被强制终止,或其它原因导致的文件损坏,可以添加 --fix 去修复。

模拟文件损坏,编辑文件后随意加载点东西
vim ../appendonly.aof

重启redis服务,发现redis服务起不来了(配置有服务,下文有讲)
service redis restart

修复,看见Successfully字眼,说明修复成功,若是redis7,请操作appendonly.aof.incr增量文件
./redis-check-aof --fix ../appendonly.aof
0x              83: Expected prefix '*', got: 'a'
AOF analyzed: size=139, ok_up_to=131, diff=8
This will shrink the AOF from 139 bytes, with 8 bytes, to 131 bytes
Continue? [y/N]: y
Successfully truncated AOF

启动redis
service redis start

AOF备份压缩

  • 简单概括:由于写命令的增加,AOF文件越来越大,恢复速度也会随之增加,因此Redis新增了重写机制,当AOF超过所设定的最大值时,Redis就会自动启动文件压缩,只保留可以恢复数据的最小有效指令集,或使用bgrewriteaof来进行压缩。
  • 核心原理:原先set a 1,再执行set a 2,一直到set a 10,初始的aof记录方式是记录10条备份,而压缩只需要记录1条set a 10,这样用于减小文件,也用于快速恢复数据,这就是最小有效的指令集。但是一开始就这样做,意味着时间换空间(更多的计算),会降低aof的性能。
  • 自动触发(需同时满足两个配置文件中的条件):
    • auto-aof-rewrite-percentage 100:上次重写之后的增长百分比,这里默认为100。
    • auto-aof-rewrite-min-size 64mb:表示aof文件超过64MB。
  • 手动触发:命令行执行bgrewriteaof命令。

双备份模式

上文有讲,默认开启的RDB和手动开启AOF,不会互相影响,两者是并列关系。
但是有备份粒度更小的AOF,redis启动时,就不会加载RDB。除非在配置文件中配置aof-use-rdb-preamble yes(实测这个配置yes或no不影响AOF+RDB的同时备份,版本5.0.9)。
这个配置值的意义是:用于控制是否在AOF文件的开头使用RDB文件的格式。当设置为yes时,Redis在AOF文件的开头会添加一个RDB文件的内容,以便在重启时快速加载数据,提高启动速度和恢复能力。

纯缓存模式

  • RDB:配置save ‘’
  • AOF:配置appendonly no
  • 注意:经过实测,仍旧可以使用bgsave或save生成rdb文件,或bgrewriteaof生成aof文件。

扩展,Redis服务编写

或许你们好奇redis的启动、关闭、重启、状态查看,上文用的service命令,其实是利用bash shell做的,如下:

touch /etc/init.d/redis
chmod +x /etc/init.d/redis
chkconfig redis on,或 systemctl enable redis.service
vim /etc/init.d/redis


#! /bin/bash
REDISPORT=6379
EXEC=/usr/local/redis/bin/redis-server
REDIS_CLI=/usr/local/redis/bin/redis-cli
   
PIDFILE=/var/run/redis.pid
CONF="/usr/local/redis/etc/redis.conf"
   
case "$1" in
    start)
        if [ -f "$PIDFILE" ]; then
            echo "$PIDFILE PID文件已存在,进程已在运行或崩溃"
        else
            echo -n "正在启动Redis..."
            $EXEC $CONF
            if [ "$?"="0" ]; then
                echo "启动成功"
            else
                echo "启动失败"
            fi
        fi
        ;;
    stop)
        if [ ! -f "$PIDFILE" ]; then
            echo "$PIDFILE PID文件不存在,没有被运行"
        else
            PID=$(cat $PIDFILE)
            echo "正在关闭Redis"
            $REDIS_CLI -p $REDISPORT shutdown
            if [ "$?"="0" ]; then
                echo "关闭成功"
            else
                echo "关闭失败"
            fi
        fi
        ;;
    restart)
        ${0} stop
        ${0} start
        ;;
    kill)
        echo "强制终止Redis进程"
        killall redis-server
        if [ "$?"="0" ]; then
            echo "终止成功"
        else
            echo "终止失败"
        fi
        ;;
    status)
        if [ -f "$PIDFILE" ]; then
            echo "Redis正在运行"
        else
            echo "Redis已停止"
        fi
        ;;
  *)
    echo "命令出错,只能执行service redis start、stop、restart、status、kill" >&2
        exit 1
esac
posted @ 2024-05-12 07:20  小松聊PHP进阶  阅读(12)  评论(0编辑  收藏  举报