什么是持久化?
简单来说持久化就是将数据保存到磁盘,让即使服务宕机、重启、断电等操作后数据仍热存在,并且是完整的。
1、为什么要持久化?
- 1、Redis是一个内存数据库,宕机之后存储在内存的数据会消失。
- 2、Redis重启之后需要恢复数据,需要提供持久化机制用于恢复数据。
Redis的持久化方式有两种:RDB方式持久化、AOF方式持久化。但值得注意的是:Redis的持久化并不能保证数据的完整性。
如果使用Redis作为数据库,数据库的数据需要保证完整性时,一定需要有一个完整的数据源(比如mysql, oracle等)。
在redis启动时,从这个完整的数据源中将数据加载到redis中。一般适用与数据量较小且不容易变化的数据,比如字典表。
使用info命令查看持久化
# Server 服务相关信息,就列举几个
redis_version:5.0.14 # 版本号
redis_mode:standalone # 启动模式 standalone表示单机 cluster表示集群
os:Linux 3.10.0-1160.11.1.el7.x86_64 x86_64 # 操作系统版本
multiplexing_api:epoll # 事件分离器reactor模式为epoll
process_id:1 # 进程IO
tcp_port:6379 # 服务端口号
hz:10 # 时间事件周期为1秒钟执行10次# Clients 客户端信息
connected_clients:4 # 已连接的客户端数量# Memory 内存相关喜喜
used_memory:8169136 # 已使用内存
maxmemory:0 # 最大内存 0 表示不限制
maxmemory_policy:noeviction # 接近最大内存的淘汰策略 noeviction表示禁止驱逐# Persistence 持久化相关配置 主要时rdb和aof的相关配置
loading:0
rdb_changes_since_last_save:50371
rdb_bgsave_in_progress:0
rdb_last_save_time:1661309348
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:0
aof_enabled:1 # 是否启动aof 1表示开启 0 表示没有开启 没有开启就是使用的rdb
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:0
aof_current_size:2580696
aof_base_size:2580591
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0# Stats 统计相关信息
total_connections_received:5
total_commands_processed:7# Replication 主从相关配置
role:master # 角色 master
connected_slaves:0 # 连接的slave数量# CPU
used_cpu_sys:238.610704# Cluster 集群配置
cluster_enabled:0 # 0表示没有开启集群# Keyspace key相关信息 db0:keys=50042表示key的数量为50042 expires表示过期的数量
db0:keys=50042,expires=0,avg_ttl=0
2、RDB持久化
RDB(Redis DataBase),是Redis默认的持久化方式,是以快照的方式进行持久化的。
2.1、什么是快照方式?
简单来说就是每隔多少秒,如果满足什么条件,就执行一次把内存的数据刷新到磁盘的操作。
2.2、快照的触发方式
- 1、满足了自定义配置的快照规则
- 2、执行了save或者bgsave命令进行了手动触发备份操作
- 3、执行flushall命令
- 4、第一次执行主从复制时,会生成一个快照
2.3、自定义配置快照生成规则
在redis.conf中配置,save多少秒内数据变了多少
# redis是基于内存的数据库,可以通过设置该值定期写入磁盘。
# 注释掉“save”这一行配置项就可以让保存数据库功能失效
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 900 1
save 300 10
save 60 10000
间隔时间越长,要求就越低。就像一个漏斗一样。这就是漏斗设计,目的是为了提升性能。
2.4、使用命令显式触发
比如:在客户端输入bgsave命令
127.0.0.1:6379> bgsave
Background saving started
2.5、RDB持久化流程
持久化过程图解
持久化过程解析
- 1、Redis父进程判断是否正在执行save,bgsave,bgrewriteaof(aof文件重写)的子进程。如果在,则直接返回。
- 2、父进程执行fork(调用操作系统复制主进程)来创建一个子进程(用于生成RDB文件),该过程父进程阻塞,Redis不i能执行来自客户端的命令。
- 3、父进程fork结束后,bgsave命令返回“Background saving started”并不再阻塞主进程,且可以开始响应来自客户端的命令。
- 4、子进程创建RDB文件,根据父进程内存进行快照生成一个临时的快照文件,完成后对原来的RDB文件进行原子替换,因此RDB始终完整。
- 5、子进程发送信号给父进程表示完成,父进程更新统计信息。
我们保存了RDB文件,那么RDB文件结构是怎么样的呢?接下来看一下RDB的结构。
2.6、RDB文件结构
- 1、第一部分:头部5个字节,固定为REDIS字符串
- 2、第二部分:4个字节指的是“RDB”版本号(注意不是Redis的版本号),比如为9,会进行填充,填充后为0009。
- 3、第三部分:辅助字段,以key-value形式存储
- redis-ver 5.0.5
- redis-bits 64/32
- ctime 当前时间戳
- used-mem 使用内存
- aof-preamble 是否开启aof
- repl-stream-db 主从复制相关参数值
- repl-id 主从复制相关参数值
- repl-offset 主从复制相关参数值
- 第四部分:存储数据库号码
- 第五部分:字典大小
- 第六部分:过期key字典大小
- 第七部分:主要数据,以key-value的形式存储
- 第八部分:结束标志
- 第九部分:校验和,就是看文件是否损坏,或者是否被修改
可以使用winhex工具打开dump.rdb查看
2.7、RDB持久化的优缺点
优点
RDB是二进制压缩文件,占用空间小,便于传输(传给slave)
主进程fork子进程,可以最大化Redis性能,主进程不能太大,复制过程中主进程阻塞
缺点
不能保证数据完整性,会丢失最后一次快照以后更改的所有数据
3、AOF持久化
3.1、AOF持久化简介
AOF(Append Only File)是Redis的另一种持久化方式。默认不开启,开启后采用AOF方式持久化。
开启之后,Redis将所有对数据库的写入命令和参数通过RESP协议记录到AOF文件,从而达到操作过程记录,持久化数据。并且可以通过该文件进行数据恢复操作。
因为Redis重启之后,只需要把这些命令解析出来,重新执行一遍就能恢复数据了。
总的来说,AOF就是记录过程,RDB就是记录结果。
3.2、AOF持久化过程分析
3.2.1、开启AOF持久化
开启aof持久化,在redis.conf中配置
############### APPEND ONLY 持久化方式 ###############
#默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,会导致可能有几分钟的数据丢失,#根据save来策略进行持久化,Append Only File是另
一种持久化方式,可以提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,#每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。
appendonly yes#aof文件名
appendfilename "appendonly.aof"
3.2.2、AOF持久化流程
AOF文件中存储的是redis的命令,从接受命令到写入AOF文件的整个过程可分为三个阶段
- 1、命令传播:Redis将执行完的命令、命令参数、参数个数等发送到AOF文件。
- 2、缓存追加:AOF程序根据接受到的数据,将命令转换为RESP协议,然后将协议内容追加到服务器的AOF缓存中。
- 3、文件写入和保存:AOF缓存中的内容被追加写入到末尾,如果设定的条件满足的话。fsync或fdatasync会被调用,将写入的的内容真正的保存到磁盘。
3.2.2.1、命令传播
- 1、当一个客户端执行Redis命令时,它通过网络连接,将RESP协议文本发送给Redis服务器。
- 2、Redis服务器接受到客户端请求后,根据协议文本内容,选择适当的命令函数,并将各个参数从字符串文本转换为Redis的字符串对象(StringObject).
- 3、命令执行结束后,命令参数会被传播到AOF程序。
3.2.2.2、缓存追加
- 1、当命令被传播到AOF程序后,程序根据命令及参数,将命令从字符串对象转换为原来的协议文本。
- 2、协议文本生成后,被追加到aof_buf(AOF缓存:保存着等待写入AOF文件的协议文本)的末尾。
3.2.2.3、文件写入和保存
当事件处理器执行时,函数aof.c/flushAppendOnlyFile都会被调用,该函数做了两件事。
- 1、WRITE:根据条件,将aof_buf中的缓存写入到AOF文件中
- 2、SAVE:根据条件,调用同fsync/fdatasync将AOF文件保存到磁盘,也叫刷盘。
既然时根据条件进行刷盘操作,那么条件是什么呢?就是AOF的保存模式。
3.2.2.4、AOF文件保存模式
文件保存模式分为三种:
- 1、AOF_FSYNC_NO: 不保存
- 2、AOF_FSYNC_EVERYSEC: 每秒钟保存一次(默认的)
- 3、AOF_FSYNC_ALWAYS:每执行一个命令保存一次(性能差,不推荐).
#aof持久化策略的配置
#no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
#always表示每次写入都执行fsync,以保证数据同步到磁盘。
#everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。
appendfsync everysec
不保存
在这种模式下,每次调用flushAppendOnlyFile函数,WRITE都会被执行,但SAVE会被略过。
每秒保存一次
在这种模式中,SAVE原则上每隔一秒钟就会执行一次, 因为SAVE操作是由后台子线程(fork)调用的,所以它不会引起服务器主进程阻塞。
每执行一个命令保存一次
在这种模式下,每次执行完一个命令之后,WRITE和SAVE都会被执行。
因SAVE是由Redis主进程执行的,所以在SAVE执行期间,主进程会被阻塞,不能接受命令请求。
AOF保存模式对性能和安全性是存在影响的。所以如果需要高性能,最好还是使用RDB。但是就不能使用AOF也保证性能吗?后面会有使用场景比较说明的。
AOF三种模式对主进程阻塞情况
可以看到丢失数据最少的也是阻塞最多的,性能最差的。所以一般都是设置为每秒保存一次。
3.2.2.5、AOF文件重写瘦身
AOF记录数据的变化过程,越来越大,并且存在大量无效的命令记录过程,因此需要重写“瘦身”。
Redis可以在AOF体积变得过大时,自动在后台(也就是fork一个子进程)对AOF进行重写。
重写后新的AOF文件就存储了能够恢复当前数据所需的最小命令集合。
重写并不是需要对原有的文件进行写入和读取,针对的是数据库中key的当前值做处理。
set name zhangsan
set name lisi
set name wangwu
可以优化为只执行最后一条命令。
Redis不希望AOF重写过程造成服务器无法处理请求,因此Redis决定将AOF重写程序使用一个后台子进程执行。
优点
- 1、子进程进行AOF重写期间,主进程可以继续处理请求
- 2、子进程带有主进程的数据副本,而不是数据本身,这样避免了锁资源竞争,保证了数据的安全性。
缺点
子进程重写AOF期间,主进程还需要继续处理命令,而新的命令可能对现有数据修改,这会导致当前数据库的数据和重写后不一致。从而增加了AOF重写缓存。
AOF重写缓存
Redis增加了一个AOF重写缓存,在主进程fork子进程后开始启用。
Redis主进程接受到新的命令后,除了将写命令的协议文本追加到AOF文件之外,还会追加到
AOF重写缓存中。
重写过程分析
Redis在创建新的AOF文件的过程,会继续将命令追加到AOF文件末尾,即使重写过程出现停机等异常情况,现有的AOF文件也不会丢失。
一旦新的AOF文件创建完成,Redis就会从旧的AOF文件切换到新的AOF文件,后续操作将会对新的AOF进行追加操作。
- 1、将命令追加到现有的AOF文件中
- 2、将命令追加到AOF重写缓存中
- fork一个子进程,将所有的命令记录到AOF重写缓存中
- 完成AOF重写后,向父进程发送一个完成信号
- 将AOF重写缓存中的内容全部写入到新的AOF文件中
- 对新的AOF文件进行改名,原子替换原有的AOF文件
只有最后的写入缓存和原子替换新旧AOF文件操作会造成主进程阻塞,在其他时候,AOF后台重写都不会对主进程造成阻塞, 这将AOF重写对性能造成的影响降到了最低。
触发重写条件
1、重写缓存配置,在redis.conf中
#aof自动重写配置。当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候Redis能够调用bgrewriteaof对日志文件进行重写。#当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。
auto-aof-rewrite-percentage 100
#设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写
auto-aof-rewrite-min-size 64mb
2、使用bgrewriteaof显式触发
127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
3.2.2.6、混合持久化
RDB和AOF各有优缺点,Redis4.0开始支持rdb和aof的混合持久化。
如果把混合持久化打开,aofrewrite的时候就直接把rdb的内容写到aof文件开头。
也就是RDB的头+AOF的身体,最终生成的文件还是appendonly.aof
开启混合持久化
# 开启混合持久化
aof-use-rdb-preamble yes
此时AOF文件是rdb文件的头和aof格式的内容,在加载时,首先会识别AOF文件是否以REDIS字符串开头,如果是就按RDB格式加载,加载完RDB后继续按AOF格式加载剩余部分。
3.2.2.7、AOF文件数据恢复
AOF文件里面包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一遍AOF文件里面保存的写命令,就可以还原服务器关闭之前的数据库状态。
Redis如何读取AOF文件并恢复数据的。
- 1、创建一个不带网络连接的伪客户端(fake client)
- 2、从AOF中分析并读取出一条写命令
- 3、使用fake client执行被读出的写命令
- 4、读完一条命令之后继续读取,并循环该过程,直到命令被fake client执行完毕为止。
为什么是创建一个不带网络连接的伪客户端?
因为Redis的命令只能早客户端的上下文执行,并且载入的文件是直接来源于AOF文件而不是网络连接。
因此,没有必要去创建网络连接,从而增加连接开销。所以创建一个不带网络连接的伪客户端执行AOF保存的命令。它的执行效果与真正的客户端完全一致,只是少了网络连接而已。
4、RDB与AOF持久化对比
- 1、RDB是某一个时刻数据的快照,使用二进制格式存储。AOF是存储的命令过程,采用文本存储。
- 2、RDB性能很高,AOF性能较低
- 3、在服务器宕机情况下,RDB会丢失最后一次快照以后更改的所有数据,AOF如果设置为每秒保存一次,最多会丢失2s的数据。
- 4、Redis以主服务器(master)模式运行,RDB不会保存过期的数据。以从服务器(slave)运行,RDB会保存过期键值对,当主从同步数据时,才会清空过期键值对。
- 5、AOF写入文件时,会对过期key增加一条del命令,当AOF重写时,会忽略过期key和del命令。
5、不同场景的持久化方式
5.1、使用Redis作为数据库使用
使用Redis作为数据库,一般是存储一些数据量小,不易变化的数据。比如:字典库。
此时为了追求高性能,什么都不用开,redis宕机,直接从数据源恢复。key的淘汰策略也应该设置为禁止驱逐,从而保证数据的完整性。
5.2、作为高性能缓存服务器
因为是缓存,所以不需要保证数据的完整性,也需要保证高性能,因此使用RDB持久化是比较合适的。
5.3、相对保证数据完整性+高性能
使用rdb+aof方式,数据即不容易丢,性能也还是不错。
5.4、只使用AOF
因为单独使用AOF性能比较低,一般不推荐。如果需要保证数据完整性,最好从指定数据源加载到Redis。而不是有定要使用Redis来保证数据完整性。
6、小结
注意:Redis的持久化并不能保证数据的完整性,持久化主要用于数据恢复。它本质上是一个高性能的缓存服务器,所以为了保证性能,是不会为了保证完整性而降低性能的。