持久化
前言
redis虽然是一个内存级别的缓存程序,也就是redis是使用内存进行数据的缓存的,但是其可以将内存的数据按照一定的策略保存到硬盘上,从而实现数据持久保存的目的
目前redis支持两种不同的保存方式的数据持久化保存机制,分别是RDB和AOF
将内存中的数据保存到磁盘中,等下次重启时在将磁盘中的缓存数据读入到内存中,生产中建议开启,因为redis在重启后在本地磁盘中直接读入缓存数据比用户直接访问数据本身在形成缓存的效率高,所以可以起到加速的效果
RDB (快照)
RDB 概述
RDB(redis database):基于时间的快照,其默认只保留当前最新一次的快照,特点是执行速度比较快,缺点是可能会丢失从上次快照到当前时间点之间未做快照的数据
某个时间点,快速的将redis内存中的数据拷贝到到磁盘中,生成一个RDB文件(二进制格式)存放的就是redis当前数据的快照
RDB 工作原理
RDB 基于内存的快照,实现方式有两种:手动执行 save 或 bgsave、在配置文件中定义自动保存规则
-
save:同步方式保存快照,因为是在主进程中执行快照 所以可能会阻塞其他命令的执行 因此不常用
-
bgsave:异步方式保存快照,首先 redis 主进程会
fork()出一个子进程,最初子进程会和父进程共享同一块内存空间,然后子进程将内存中的数据dump到RDB临时文件中,当数据写完后 临时文件会覆盖掉旧的RDB文件-
如果快照过程中父进程的内容发生更改,那么父进程就会基于写时复制机制复制一块内存空间用于存储新的数据,从而避免原有的内存数据发生变化而影响到子进程的快照执行(如果子进程存在期间,发生了大量的写操作,那可能就会出现很多的分页错误(页异常中断page-fault),这样就得耗费不少性能在复制上。)
-
快照执行期间主进程所发生的数据修改不会保存到快照当中,因为变化的数据被写时复制机制写入到了一块独立分配的内存空间
-
当redis服务重启后,会重新读取RDB文件 并将RDB文件中的数据读入到内存中
-
-
配置文件中定义:save 间隔多少秒 多少个key发生变化(并且的关系)
-
临时文件:temp.PID.rdb
-
最终文件:dump.rdb
RDB 优点
- RDB快照保存了某个时间点的数据,可以通过脚本执行redis指令bgsave(非阻塞,后台执行)或者save(会阻塞写操作,不建议使用)命令自定义时间点备份,可以保留多个备份,当出现问题可以恢复到不同时间的版本,很适合备份,并且此文件格式也支持有不少第三方工具 可以进行后续的数据分析
- RDB可以最大化redis的性能,父进程在保存rdb文件时唯一要做的就是fork出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无需执行任何磁盘i/o操作
- RDB在大量数据,比如几个G的数据,恢复的速度比AOF的快
RDB 缺点
- 不能实时保存数据,可能会丢失自上一次执行RDB备份到当前的内存数据
如果需要尽量避免在服务器故障时丢失数据,那么RDB不合适。虽然redis允许设置不同的保存点(save point)来控制保存RDB文件的频率,但是,因为RDB文件需要保存整个数据集的状态,所以它并不是一个轻松的操作。因此你可能会至少5分钟才保存一次RDB文件。在这种情况下,一旦发生故障停机,那就可能会丢失好几分钟的数据
- 当数据量非常大的时候,从父进程fork子进程进行保存至RDB文件时需要一点时间,可能是秒或者毫秒,取决于磁盘的I/O性能;在数据集比较庞大时,fork()可能会非常耗时,造成服务器在一定时间内停止处理客户端;如果数据集非常巨大,并且CPU时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒或更久。虽然AOF重写也需要fork(),但无论AOF重写的执行间隔有多长,数据的持久性都不会有任何损失
RDB 实现方式
手动执行:
- save,同步,会阻塞其他命令,不推荐使用
- bgsave,异步后台执行,不影响其他命令的执行
自动执行:
- 需要在配置文件中自定义执行规则
RDB 相关配置
[root@aliyun ~]# vim /etc/redis.conf
# 自动保存规则(别看选项名叫 save,实际上执行的是 bgsave 命令,也就是会创建子进程来生成 RDB 快照文件。)
save "" # 只留这行,然后将下面三行注释掉,表示关闭自动保存
save 900 1 # 在900秒内有1个key内容发生更改,就执行快照机制
save 300 10 # 在300秒内有10个key内容发生更改,就执行快照机制
save 60 10000 # 在60秒内有10000个key以上的变化,就自动快照备份
dbfilename dump.rdb #快照文件名
dir ./ #rdb或aof的文件路径,示例:dir "/apps/redis/data"
stop-writes-on-bgsave-error no #默认为yes时,可能会因空间满等原因快照无法保存出错时,会禁止redis写入操作,生产建议为no,此项只针对配置文件中的自动save有效
rdbcompression yes #持久化到RDB文件时,是否压缩,"yes"为压缩,"no"则反之
rdbchecksum yes # 是否对备份文件开启rc64效验,默认是开启(使用rdb恢复数据时是否检查rdb文件是否已经损坏)RDB 的还原
- 直接将备份的RDB文件拷贝到配置文件指定的路径中(即配置文件中 dir 所指向的路径),并同名 同权限即可,重启后会自动还原
RDB 备份
- 因为RDB默认只保存最新一次的快照,所以生产中建议给RDB做备份
- **生产中的备份思路:**在计划任务中写一个bgsave的备份脚本,备份完后将rdb文件拷贝到指定的备份目录中并加上时间戳,要注意bgsave并不是立刻备份完等问题,可以通过判断info的参考值是否为0来决定是否拷贝
RDB 备份脚本
- 此脚本可以放到计划任务中,定时执行,以达到分时备份的效果
[root@8 ~]# cat redis_rdb.sh
#!/bin/bash
#
#********************************************************************
#Author: xiangzheng
#QQ: 767483070
#Date: 2021-12-25
#FileName: redis_rdb.sh
#URL: https://www.xiangzheng.vip
#Email: rootroot25@163.com
#Description: The test script
#Copyright (C): 2021 All rights reserved
#********************************************************************
HOST="10.0.0.8"
PASS="321"
SOUDIR="/apps/redis/data/"
FILE="dump.rdb"
DIR="/data/redis"
TYPE="bgsave"
redis-cli -h ${HOST} -a ${PASS} --no-auth-warning ${TYPE} &>/dev/null
RESULT=`redis-cli -h ${HOST} -a ${PASS} --no-auth-warning info persistence | grep rdb_bgsave_in_progress | awk -F: '{print $2}' | tr -dc '[:digit:]'`
until [ ${RESULT} -eq 0 ] ;do
sleep 1
RESULT=`redis-cli -h ${HOST} -a ${PASS} --no-auth-warning info persistence | grep rdb_bgsave_in_progress | awk -F: '{print $2}' | tr -dc '[:digit:]'`
done
DATE=`date +%F-%T`
[ -d ${DIR} ] || mkdir -p ${DIR}
cp -a ${SOUDIR}${FILE} ${DIR}/${DATE}-${FILE}—
AOF (类似写二进制日志)
AOF 概述
-
AOF:AppendOnyFile,类似二进制日志,按照操作顺序依次将操作追加到指定的日志文件末尾(效率高)
-
同时开启RDB和AOF的情况下,AOF的优先级高
-
AOF保存为文本可见格式
AOF 工作原理
AOF类似写二进制日志,redis server会fork出一个子进程,然后使用写时复制机制(copy on write),父进程继续提供读写服务 子进程将内存中的数据按照操作顺序追加到AOF临时文件中(临时文件的名称为temp-rewriteaof-子进程的PID.aof),当数据写完后 临时文件覆盖掉旧的AOF文件,当redis服务重启后,会重新读取AOF文件 并将AOF文件中的数据读入到内存中
AOF 优点
-
数据的安全性相对较高,根据所使用的fsync策略(fsync是同步内存中redis所有已经修改的文件到存储设备),默认是appendfsync everysec,即每秒同步一次,这样redis既可以保持很好的性能,并且遇到停电 宕机等情况只会最多丢失一秒的数据(fsync会在后台线程执行,所以主线程可以继续努力地处理命令请求),如果一秒的数据丢失都不能忍受的话,则需开启appendfsync always,即数据更新立刻写入磁盘,但这样会牺牲性能来换取数据的安全
-
由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中不需要seek,即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果本次操作只是写入了一般数据就出现了系统崩溃等问题,不用担心,在redis下一次启动之前,可以通过redis-check-aof工具来解决数据一致性的问题
-
redis可以在AOF文件变得过大时,自动整理其中的数据,将已经删除的数据情况,从而达到缩小文件体积的目的,整个重写操作是绝对安全的,因为redis在创建新的AOF文件的过程中,append模式会不断的将修改数据追加到现有的AOF文件里面,即使重写过程中发生停机,现有的AOF文件也不会丢失,一旦新的AOF文件创建完毕,redis就会从旧AOF文件切换到新的AOF文件,并开始对新AOF文件进行追加操作
-
AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,也可以通过修改该文件完成数据的重建,假设:如果不小心执行了FLUSHALL命令,但只有AOF文件未被重写,那么只要停止服务器,移除AOF文件末尾的FLUSHALL命令,并重启redis,就可以将数据恢复到FLUSHALL执行之前的状态
AOF 缺点
- 因为有些会重复、多余记录,所以AOF文件的大小要大于RDB文件
- AOF在恢复大数据时的速度要比RDB文件要慢
- 根据fsync策略不同,AOF的速度可能会慢于RDB
- bug出现的可能性更多
AOF 相关配置
# /etc/redis.conf
...
appendonly no # 是否开启AOF,yes开启,no关闭,默认为no
appendfilename "appendonly.aof" # 默认AOF文件的名称
appendfsync everysec # 数据更新后aof多久写一次磁盘,默认值everysec
# everysec 每秒写一次 生产建议值 速度与安全兼顾
# always 发生修改立即写入磁盘,数据安全,但会牺牲性能
# no 由操作系统保证数据同步到磁盘,Linux的默认fsync策略是30秒
dir ./ # rdb或aof的文件路径
#同时在执行bgrwriteaof操作和主进程写aof文件的操作的情况下,两者都会操作磁盘,而bgrewriteaof往往会涉及大量磁盘操作,这样就会造成主进程在写aof文件的时候出现阻塞的情形,以下参数实现控制
no-appendfsync-on-rewrite no #在aof rewrite期间,是否对aof新纪录的append暂缓使用文件同步策略,主要考虑磁盘IO开支和请求阻塞时间
#默认为no,表示"不暂缓",新的aof记录仍然会被立即同步到磁盘,是最安全的方式,不会丢失数据,但是要忍受阻塞问题
#为yes,相当于将appendfsync设置为no,这说明并没有执行磁盘操作,只是写入了缓冲区,因此这样并不会造成阻塞(因为没有磁盘竞争),但是如果这个时候redis挂掉,就会丢失数据。丢失多少数据呢?Linux的默认fsync策略是30秒,最多会丢失30秒的数据,但由于yes性能较好而且会避免出现阻塞因此比较推荐
# rewrite 相关设置(将一些 重复的、可以合并的、过期的数据 重新写入一个新的AOF文件,从而节约AOF备份占用的磁盘空间,也能加速恢复过程)
auto-aof-rewrite-percentage 100 #当AOF log增长超过指定百分比例时,重写AOF文件,设置为0表示不自动重写AOF日志,重写是为了使AOF体积保持最小,但是还可以确保保存最完整的数据
auto-aof-rewrite-min-size 64mb #触发aof rewrite的最小文件大小
#整理说明,假设设置的为以上值 则:当第一次数据达到64M就会发生整理 假设整理后的空间为50M 那么等下次还会在数据达到64M的时候进行整理;如果数据再次整理后的空间超过64M的话 会在128M时进行整理 假设整理后的空间为100M 则下次会在数据200M时进行整理;
aof-load-truncated yes # 是否加载由于某些原因导致的末尾异常的AOF文件(主进程被kill/断电等),建议yes
aof-use-rdb-preamble yes # RDB-AOF混合持久化模式,在开启了这个功能之后,AOF重写产生的文件将同时包含RDB格式的内容和AOF格式的内容,其中RDB格式的内容用于记录已有的数据,而AOF格式的内容则用于记录最近发生了变化的数据,这样redis就可以同时兼有RDB持久化和AOF持久化的优点(既能够快速的生成重写文件,也能够在出现问题时,快速地载入数据),yes为开启,no为不开启
...AOF rewrite
-
将一些 重复的、可以合并的、过期的数据 重新写入一个新的AOF文件,从而节约AOF备份占用的磁盘空间,也能加速恢复过程
-
比如删除数据只是在AOF文件中标记删除记号,而不是真正的删除
-
手动触发:可以手动执行 BGREWRITEAOF 触发AOF重写
# 执行此命令即可手动同步AOF文件内容,将没有用的内容清空,减少文件的占有空间
127.0.0.1:6379> BGREWRITEAOF- 自动触发:定义自动 rewrite 策略(自动执行BGREWRITEAOF)
# /etc/redis.conf
...
auto-aof-rewrite-percentage 100 # 当AOF log增长超过指定百分比例时,重写AOF文件,设置为0表示不自动重写AOF日志,重写是为了使AOF体积保持最小,但是还可以确保保存最完整的数据
auto-aof-rewrite-min-size 64mb #触发aof rewrite的最小文件大小
# 整理说明,假设设置的为以上值 则:当第一次数据达到64M就会发生整理 假设整理后的空间为50M 那么等下次还会在数据达到64M的时候进行整理;如果数据再次整理后的空间超过64M的话 会在128M时进行整理 假设整理后的空间为100M 则下次会在数据200M时进行整理;
...从RDB转变成AOF的正确方法
- 如果直接开启AOF,重启redis服务后,因为AOF优先级高,所以会因为AOF文件没有内容,而导致redis的内容被清空
正确方法:
# 1:先用config查看是否开启AOF
127.0.0.1:6379> CONFIG GET appendonly
1) "appendonly"
2) "no"
# 2:再用config动态开启AOF(这样就会将内存中的数据直接生成AOF文件)
127.0.0.1:6379> CONFIG SET appendonly yes
OK
# 3:最后在修改配置文件
[root@aliyun ~]# vim /etc/redis.conf
appendonly yes
# 下次重启redis时 因为已经实现动态开启了AOF 并且生成了AOF文件 所以会直接加载AOF文件中的数据 避免了数据丢失RDB 和 AOF 对比
- 数据安全性,AOF 要高于 RDB
- 因为 AOF 是以追加的方式写入数据,并且还可以设置每秒写入一次磁盘 或 数据只要发生更改就写入磁盘 从而实现最高级别的容错
- 而 RDB 是以快照方式保存数据,在数据量较大时 如果频繁拍摄快照会对 redis 的性能造成很大的影响
- 文件易读性,AOF 要高于 RDB
- 因为 AOF 是明文的命令 可以进行很清晰的展示 可以直接对其进行修改,而 RDB 是二进制乱码格式 无法手动修改
- 数据恢复速度,RDB 要高于 AOF
- 因为 AOF 需要以执行每条命令的方式进行数据还原,而 RDB 是直接将数据读入内存
- 文件大小,AOF 要大于 RDB
- 因为 AOF 文件会有重复、冗余的记录,所以文件会比RDB的要大
- 如果同时存在AOF和RDB文件的话,重启redis优先恢复AOF中的数据
RDB 和 AOF 的选择
- 还是需要根据生产环境以及实际情况来选择
- 如果主要充当缓存功能,或者可以承受数分钟的数据丢失,通常生产环境一般只需启用RDB即可,此也是默认值
- 如果数据需要持久保存,一点也不能丢失,可以选择同时开启RDB和AOF,一般不建议只开AOF