Redis原理(二)持久化

一.持久化的意义

redis是内存数据库,进程退出之后为保证数据能恢复就有了持久化。redis持久化分为下面两种:

  1. RDB :将当前的数据压缩保存到硬盘。
  2. AOF: 将每次执行的写命令保存到硬盘(类似mysql的binlog)。

二.RDB持久化

1.触发条件

(1) 手动执行命令触发
save: save命令会阻塞redis主进程,知道RDB完毕,线上应该杜绝。
bgsave: 会fork一个子进程来执行,只有fork子进程时会阻塞redis主进程,其余都是不影响redis主进程的。
SHUTDOWN命令也会触发。
在这里插入图片描述
(2) 自动触发 save m n
在配置文件中配置 save m n 指定在m秒内数据发生n次变化就执行rdb持久化。
在这里插入图片描述
上图表示:三种情况任意一种满足就执行bgsave。

2.save m n的原理

redis通过serverCron函数、dirty计数器、和lastsave时间戳来实现save m n的。

serverCron函数
是redis的一个定时器,每隔100ms执行一次,redis停止,则停止,用来维护redis服务的状态,其中有一项就是:检查save m n 配置条件是否满足,满足就执行bgsave。

dirty计数器
用来标记上一次save/bgsave执行后,服务器进行了多少次修改,增加,删除操作 。当执行save/bgsave后就置为0。

lastsave时间戳
记录的是上一次成功执行save/bgsave的时间。

save m n的原理如下
每隔100ms,执行serverCron函数;在serverCron函数中,遍历save m n配置的保存条件,只要有一个条件满足,就进行bgsave。对于每一个save m n条件,只有下面两条同时满足时才算满足:
(1)当前时间-lastsave > m
(2)dirty >= n

3.RDB执行流程
  1. redis主进程判断:当前是否存在正在执行bgsave的子进程,有的话直接返回,没有则继续如下。
  2. 主进程fork出子进程,此时主进程阻塞,redis不能进行任何命令操作。
  3. fork完成后,主进程恢复。子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后调用rename 将临时文件改名正式的 RDB 文件。
  4. 子进程发送信号给父进程完成,父进程更新统计信息。

(1) fork时的注意?
fork 会消耗一定时间,并且父子进程所占据的内存是相同的,当 Redis 存储的数据较大时fork 的时间会很长,这段时间内 Redis 是无法响应其他命令的。除此之外,Redis 占据的内存空间也会翻倍

(2) 为什么redis不启动线程来执行rdb操作?
如果在主进程内启动一个线程,这样会造成对数据的竞争条件。所以为了避免使用锁降低性能,Redis选择启动新的子进程,独立拥有一份父进程的内存拷贝,以此为基础执行RDB持久化。

4.RDB文件

(1) 存储路径
redis.conf存储路径设置

## 指定目录
dir "/usr/local/yunji/redis/data"
# 指定rdb文件
dbfilename "dump-14159.rdb"

运行时动态改变路径
在这里插入图片描述

(2) RDB文件格式

----------------------------# RDB文件是二进制的,所以并不存在回车换行来分隔一行一行.
52 45 44 49 53              # 以字符串 "REDIS" 开头
30 30 30 33                 # RDB 的版本号,大端存储,比如左边这个表示版本号为0003
----------------------------
FE 00                       # FE = FE表示数据库编号,Redis支持多个库,以数字编号,这里00表示第0个数据库
----------------------------# Key-Value 对存储开始了
FD $length-encoding         # FD 表示过期时间,过期时间是用 length encoding 编码存储的
$value-type                 # 1 个字节用于表示value的类型,比如set,hash,list,zset等
$string-encoded-key         # Key 值,通过string encoding 编码
$encoded-value              # Value值,根据不同的Value类型采用不同的编码方式
----------------------------
FC $length-encoding         # FC 表示毫秒级的过期时间
$value-type                 # 同上,也是一个字节的value类型
$string-encoded-key         # 同样是以 string encoding 编码的 Key值
$encoded-value              # 同样是以对应的数据类型编码的 Value 值
----------------------------
$value-type                 # 下面是没有过期时间设置的 Key-Value对,为防止冲突,数据类型不会以 FD, FC, FE, FF 开头
$string-encoded-key
$encoded-value
----------------------------
FE $length-encoding         # 下一个库开始,库的编号用 length encoding 编码
----------------------------
...                         # 继续存储这个数据库的 Key-Value 对
FF                          ## FF:RDB文件结束的标志

(3) RDB常用配置

  • stop-writes-on-bgsave-error yes:当bgsave出现错误时,Redis是否停止执行写命令;设置为yes,则当硬盘出现问题时,可以及时发现,避免数据的大量丢失;设置为no,则Redis无视bgsave的错误继续执行写命令。
  • rdbcompression yes:是否开启RDB文件压缩。
  • rdbchecksum yes:是否开启RDB文件的校验,在写入文件和读取文件时都起作用;关闭checksum在写入文件和启动文件时大约能带来10%的性能提升,但是数据损坏时无法发现。
  • save m n:bgsave自动触发的条件。
  • dbfilename dump.rdb:RDB文件名。
  • dir :RDB文件和AOF文件所在目录。

(4) 线上建议
RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据备份。非常适合备份,全量复制等场景。比如每6小时执行 bgsave 备份,并把 RDB 文件拷贝到远程机器或者文件系统中,用于灾难恢复。

三.AOF持久化

开启aof

##开启aof
appendonly yes
## aof文件名称
appendfilename appendonly.aof
## 目录
dir /usr/local/redis/data 

文件格式

*2     # 2个参数
$6     # 第一个参数长度为 6
SELECT     # 第一个参数
$1     # 第二参数长度为 1
8     # 第二参数
*3     # 3个参数
$3     # 第一个参数长度为 4
SET     # 第一个参数
$4     # 第二参数长度为 4
name     # 第二个参数
$4     # 第三个参数长度为 4
Jhon     # 第二参数长度为 4

上面aof内容就对应 redis 命令:SELECT 8;SET name Jhon.

执行流程

  • 命令追加(append):将Redis的写命令追加到缓冲区aof_buf;
  • 文件写入(write)和文件同步(sync):根据不同的同步策略将aof_buf中的内容同步到硬盘;
  • 文件重写(rewrite):定期重写AOF文件,达到压缩的目的。

命令追加(append)
redis先将命令放到缓冲区,然后在写入磁盘,避免了频繁写磁盘。

文件写入(write)和文件同步(sync)
为了提高写入效率,当用户调用write函数将数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区被填满或超过了指定时限后,才真正将缓冲区的数据写入到硬盘里。如果计算机停机,内存缓冲区中的数据会丢失;因此系统同时提供了fsync、fdatasync等同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性。
AOF缓存区的同步文件策略由参数appendfsync控制,各个值的含义如下:

  • always:命令写入aof_buf后立即调用系统fsync操作同步到AOF文件。这种情况下,每次有写命令都要同步到AOF文件。
  • no:命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步;同步由操作系统负责,通常同步周期为30秒
  • everysec:命令写入aof_buf后调用系统write操作;fsync同步文件操作由专门的线程每秒调用一次。everysec是前述两种策略的折中,是性能和数据安全性的平衡,因此是Redis的默认配置,也是我们推荐的配置。

文件重写(rewrite)
时间越长Redis写命令越来越多,AOF会越来越大。于是Redis在指定时间内,会重写AOF文件,需要注意的是,AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取、写入操作!

# 假设服务器对键list执行了以下命令s;
127.0.0.1:6379> RPUSH list "A" "B"
(integer) 2
127.0.0.1:6379> RPUSH list "C"
(integer) 3
127.0.0.1:6379> RPUSH list "D" "E"
(integer) 5
127.0.0.1:6379> LPOP list
"A"
127.0.0.1:6379> LPOP list
"B"
127.0.0.1:6379> RPUSH list "F" "G"
(integer) 5
127.0.0.1:6379> LRANGE list 0 -1
1) "C"
2) "D"
3) "E"
4) "F"
5) "G"

当前列表键list在数据库中的值就为[“C”, “D”, “E”, “F”, “G”]。要使用尽量少的命令来记录list键的状态,最简单的方式不是去读取和分析现有AOF文件的内容,而是直接读取list键在数据库中的当前值,然后用一条RPUSH list “C” “D” “E” “F” "G"代替前面的6条命令。
为了避免执行命令时造成客户端输入缓冲区溢出(个数过多),Redis会指定一个常量最大值64,即每条命令设置的元素的个数最多64。

文件重写触发

  • 手动触发: 直接执行bgrewriteaof命令,只有fork子进程时阻塞。
  • 自动触发: 下面两个参数同时达到条件时才触发。
    auto-aof-rewrite-min-size:执行AOF重写时,文件的最小体积,默认值为64MB。
    auto-aof-rewrite-percentage:执行AOF重写时,当前AOF大小(即aof_current_size)和上一次重写时AOF大小(aof_base_size)的比值。

线上配置

auto-aof-rewrite-min-size 64mb
auto-aof-rewrite-percentage 40

文件重写的流程

  1. Redis主进程判断是否存在bgsave/bgrewriteaof的子进程,有直接返回,否则继续进行。
  2. Redis主进程fork出子进程,此时父进程阻塞。fork出的子进程只是有当时主进程的内存数据,由于主进程还在响应命令,因此Redis使用aof_rewrite_buf缓冲区保存这部分数据。
  3. 子进程执行bgrewriteaof操作,根据内存快照生成新的AOF文件,完成后向主进程发送信号,更新统计信息等。
  4. 主进程把aof_rewrite_buf缓冲区的数据写到新的AOF文件中,这样就保持了与当前Redis一致。
  5. 使用新的AOF文件替换成老的,完成AOF重写。

AOF常用配置

  • appendonly no:是否开启AOF。
  • appendfilename “appendonly.aof”:AOF文件名。
  • dir:RDB文件和AOF文件所在目录。
  • appendfsync everysec:fsync持久化策略。
  • no-appendfsync-on-rewrite:AOF重写期间是否禁止fsync;如果开启该选项,可以减轻文件重写时CPU和硬盘的负载(尤其是硬盘),但是可能会丢失AOF重写期间的数据;需要在负载和安全性之间进行平衡。
  • auto-aof-rewrite-percentage 100:文件重写触发条件之一。
  • auto-aof-rewrite-min-size 64mb:文件重写触发提交之一。
  • aof-load-truncated yes:如果AOF文件结尾损坏,Redis启动时是否仍载入AOF文件。

关于 no-appendfsync-on-rewrite 配置

AOF其实分为两个步骤:

  • fsync :是把内存中的写操作写入aof文件中(everysec模式每1s写一次磁盘),是在主进程进行的。
  • rewrite:是将写操作合并,是在子进程进行的。

no-appendfsync-on-rewrite no 表示 rewrite时 ,主进程fsync继续写磁盘。如果内存数据过大,会导致子进程rewrite占用大量的磁盘IO,这样会影响主进程fsync从而阻塞主进程。
线上应该配置no-appendfsync-on-rewrite yes

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值