Redis持久化
持久化 : 将瞬时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)
RDB介绍
-
Redis数据库文件, 全称Redis DataBase
- 数据持久化方式之一
- 数据持久化默认方式
- 按照指定时间间隔, 将内存中的数据集快照写入硬盘
-
RDB文件名(主配置文件默认)
- dbfilename dump.rdb
RDB优缺点
-
RDB优点
- RDB是一个非常紧凑的文件, 保存了某个时间段的数据集, 非常适合于数据集的备份
- 高性能的持久化实现 : 创建一个子进程来执行持久化, 先将数据写入临时文件, 持久化过程结束后, 再用这个临时文件替换上次持久化好的文件; 过程中主进程不做任何IO操作
- 比较适合大规模数据恢复, 且对数据完整性要求不是非常高的场合
-
RDB的缺点
- 意外宕机时, 丢失最后一次持久化的所有数据
- RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高(CPU消耗)
RDB工作过程
- 当用户执行bgsave命令, 保存数据时
- Redis父进程判断当前是否存在正在执行的子进程, 如RDB/AOF子进程, 若存在则bgsave命令直接返回
- 若不存在, 则父进程调用forks, 生成子进程 fork操作中父进程会阻塞(通过info stats命令 查看 latest_fork_usec选项, 可以获取最近一个fork操作的耗时, 单位为微秒)
- 父进程fork完后, 界面显示Background saving started 信息不再阻塞父进程, 父进程可继续响应其他命令
- 子进程将瞬时数据集写入到一个临时RDB文件中
- 当子进程完成对新RDB文件的写入时, Redis用新RDB文件替换原来的RDB文件, 并删除旧RDB文件
- 进程发送信号给父进程表示完成, 父进程更新统计信息(详细信息见info Persistence)
优化设置
- 数据从内存保存到硬盘的频率
[root@redisClient ~]# head -n 221 /etc/redis/6379.conf | tail -3
save 900 1 # 15分钟且有1个key改变即存盘
save 300 10 # 5分钟且有10个key改变即存盘
save 60 10000 # 1 分钟且有10000个key改变即存盘
- 手动存盘
[root@redisClient ~]# redis-cli
127.0.0.1:6379> save # 阻塞当前Redis服务器,直到RDB过程完成为止,对于内存 比较大的实例会造成长时间阻塞,线上环境不建议使用
OK
127.0.0.1:6379> bgsave # Redis进程执行fork操作创建子进程,RDB持久化过程由子 进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短
Background saving started #父进程fork完成后,返回Background saving started , 信息不再阻塞父进程
127.0.0.1:6379> INFO stats # 查看状态信息
... ...
latest_fork_usec:975 # 上次fork时间为975微秒
... ...
127.0.0.1:6379> info Persistence
... ...
rdb_last_save_time:1576754896 # 上次生成RDB时间
... ....
使用RDB文件恢复数据
- 备份数据
- 备份dump.rdb文件到其他位置
# 在主配置文件中查看数据库目录
[root@redisClient ~]# vim /etc/redis/6379.conf
... ...
dbfilename dump.rdb # 定义的RDB文件名
... ...
dir /var/lib/redis/6379 # RDB文件存储的目录
... ...
# 拷贝dump.rdb到其他位置
[root@redisClient ~]# cp /var/lib/redis/6379/dump.rdb .
- 恢复数据
- 拷贝备份文件到数据库目录, 启动redis服务
# 将刚刚备份的数据拷贝给另一台搭有redis的主机(此处选择MGM主机)
[root@redisClient ~]# scp dump.rdb 192.168.4.57:
# 确认MGM的redis服务可用
# 停止服务
[root@MGM ~]# /etc/init.d/redis_6379 stop
# 将dump.rdb文件拷贝到MGM的/var/lib/redis/6379/
[root@MGM ~]# cp dump.rdb /var/lib/redis/6379/
# 启动服务
[root@MGM ~]# /etc/init.d/redis_6379 start
[root@MGM ~]# redis-cli
127.0.0.1:6379> keys *
1) "site"
2) "list"
AOF介绍
- Append Only File
- 追加方式记录写操作的文件
- 记录redis服务所有写操作(set,del,add等)
- 不断的将新的写操作, 追加到文件末尾
- 默认没有启用
- 使用cat命令可以查看文件内容
AOF优缺点
- AOF优点
- 可以灵活设置持久化方式
- 出现意外宕机时, 仅可能丢失1秒的数据
- AOF缺点
- 持久化文件的体积通常会大于RDB方式
- 执行fsync策略时的速度可能会比RDB方式慢
AOF工作过程
- AOF持久化功能实现分为命令追加(append), 文件写入和文件同步sync,文件重写三个步骤
- 服务器在执行完一个写命令之后, 会以协议格式被执行的写命令追求服务器状态的aof_buf缓冲区的末尾
- redis每次追加完内容到aof_buf缓冲后会调用flushAppendOnlyFile同步函数进行将数据同步到磁盘里
- flushAppendOnlyFile函数的行为由服务器的appendfsync选项决定
文件的写入和同步
为了提高文件的写入效率, 在现代操作系统中, 当用户调用write函数, 将一些数据写入到文件的时候, 操作系统通常会将写入数据暂时保存在一个内存缓冲区里面
等到缓冲区的空间被填满、或者超过了指定的时限之后,才真正地将缓冲区中的数据的数据写入到磁盘里面
这样做将会导致如果计算机发生宕机, 那么保存在内存缓冲区里面的写入数据将会丢失
为此, 系统提供了fsync和fdataync两个同步函数, 他们可以强制让操作系统立即将缓冲区中的数据写入到硬盘里面, 从而确保写入数据的安全性
AOF文件重写
- 当Redis服务器执行的写命令越来越多, AOF文件也会越来越大 过大的AOF文件会影响服务器的正常运行同时也会导致数据恢复需要的时间过长
- 因此需要文件重写, AOF重写是把redis进程内的数据转化为写命令, 同步到新的AOF文件, 不会对旧的AOF文件进行任何读取或写入操作
- 文件重写流程 :
- Redis父进程首先判断当前是否存在正在执行 bgsave/bgrewriteaof的子进程,如果存在则bgrewriteaof命令直接返回,如果存在bgsave命令则等bgsave执行完成后再执行。
- 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的
- 父进程fork后,bgrewriteaof命令返回”Background append only file rewrite started”信息并不再阻塞父进程,并可以响应其他命令。Redis的所有写命令依然写入AOF缓冲区,并根据appendfsync策略同步到硬盘,保证原有AOF机制的正确
- 由于fork操作使用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然在响应命令,因此Redis使用AOF重写缓冲区(图中的aof_rewrite_buf)保存这部分数据,防止新AOF文件生成期间丢失这部分数据。也就是说,bgrewriteaof执行期间,Redis的写命令同时追加到aof_buf和aof_rewirte_buf两个缓冲区
- 子进程根据内存快照,按照命令合并规则写入到新的AOF文件
- 子进程写完新的AOF文件后,向父进程发信号,父进程更新统计信息,具体可以通过info persistence查看
- 父进程把AOF重写缓冲区的数据写入到新的AOF文件,这样就保证了新AOF文件所保存的数据库状态和服务器当前状态一致
- 使用新的AOF文件替换老文件,完成AOF重写
优化配置
- 定义文件名
[root@MGM ~]# vim +674 /etc/redis/6379.conf
... ...
appendonly yes # 启用aof, 默认no
appendfilename "appendonly.aof" # 指定文件名
... ...
- AOF文件记录写操作的方式
appendfsync always # 实时记录, 并完成磁盘同步(将aof_buf缓冲区中的所有内容写入并同步到AOF文件中)
appendfsync everysec # 每秒记录一次, 并完成磁盘同步(将aof_buf缓冲去中所有内容写入到AOF文件中, 每间隔一秒, 则再次对AOF文件进行同步, 且同步过程由一个线程专门负责执行的)
appendfsync no # 写入aof, 不执行磁盘同步(将aof_buf缓冲区中的所有内容写入到AOF文件, 但并不对AOF文件进行同步)
- 日志文件会不断增大, 何时触发日志重写?
# 手动触发
[root@MGM ~]# redis-cli
127.0.0.1:6379> BGREWRITEAOF # 该命令同bgsave类似 都是fork子进程进行具体的工作,且都只有在fork时阻塞
Background append only file rewriting started
# 自动触发, 根据主配置文件中 auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数
auto-aof-rewrite-min-size 64mb # 执行AOF重写时,文件的最小体积,默认值为64MB
auto-aof-rewrite-percentage 100 # 执行AOF重写时,当前AOF大小(即aof_current_size)和上一次重写时AOF大小(aof_base_size)的比值
- 修复AOF文件
- 把文件恢复到最后一次的正确操作
# 比如AOF文件受损
[root@MGM ~]# echo 1 >> /var/lib/redis/6379/appendonly.aof
[root@MGM ~]# redis-check-aof --fix /var/lib/redis/6379/appendonly.aof # 恢复AOF文件
0x 13e: Expected prefix '*', got: '1'
AOF analyzed: size=320, ok_up_to=318, diff=2
This will shrink the AOF from 320 bytes, with 2 bytes, to 318 bytes
Continue? [y/N]: y
Successfully truncated AOF
使用AOF文件恢复数据
- 启用AOF
[root@redisClient ~]# redis-cli # 不停服务启用AOF
127.0.0.1:6379> config set appendonly yes # 临时启用
OK
127.0.0.1:6379> config rewrite # 写入配置文件, 永久配置
OK
- 备份数据
- 备份appendonly.aof文件到其他位置
[root@redisClient ~]# redis-cli
127.0.0.1:6379> mset a 0 b 1 c 2 d 3 # 存储数据, redis实时生成aof文件
OK
127.0.0.1:6379> exit
[root@redisClient ~]# ls /var/lib/redis/6379/appendonly.aof
/var/lib/redis/6379/appendonly.aof
- 恢复数据
- 拷贝备份文件到数据库目录
- 启动redis服务
# 开启另一台主机的aof功能
[root@MGM ~]# redis-cli
127.0.0.1:6379> CONFIG set appendonly yes
OK
127.0.0.1:6379> CONFIG REWRITE
OK
# 停止MGM的redis服务, 避免拷贝后重启服务 覆盖掉拷贝过来的文件
[root@MGM ~]# /etc/init.d/redis_6379 stop
# 将数据拷贝给另一台搭有redis服务的主机
[root@redisClient ~]# scp /var/lib/redis/6379/appendonly.aof 192.168.4.57:/var/lib/redis/6379/appendonly.aof
# MGM启动服务
[root@MGM ~]# /etc/init.d/redis_6379 start
# 查看
[root@MGM ~]# redis-cli
127.0.0.1:6379> keys * # 成功恢复数据
1) "b"
2) "site"
3) "a"
4) "c"
5) "d"
6) "list"