什么是持久化
redis所有数据保持在内存中,对数据的更新将异步地保存在磁盘上。
持久化方式
快照
1.MySQL DUMP
2.Redis RDB
写日志
1.MySql Binlog
2.Hbase HLog
3.Redis AOFRedis持久化的取舍和选择
RDB
什么是RDB
触发机制-主要三种方式
触发机制-不容忽略方式
试验
RDB文件的创建
有两个生成redisRDB文件的命令,一个是SAVE,一个是BGSAVE。SAVE命令是使用服务器进程来生成RDB文件,在生成RDB文件的时候,会阻塞所有的读写操作,服务器不能处理任何命令请求。BGSAVE命令是同过服务器进程派生出来一个子进程,然后有子进程负责创建RBD文件,服务器进程可以继续处理命令请求。
其实创建RDB文件的工作都是通过rdbsave函数实现的是不过两种命令以不同的方式调用这个函数。
SAVE命令执行时的服务器状态
其实这个很简单,我们上面已经提到过SAVE命令或阻塞一切请求,当然拥抱口BGSAVE请求了,所以在执行SAVE期间,再次执行BGSAVE命令会被直接拒绝。
BGSAVE命令执行时的服务器状态
我们知道BGSAVE命令是服务器的子进程来完成的,服务器进程可以继续接收和执行命令,但是再BGSAVE命令执行期间服务器处理SAVE、BGSAVE、BGREWRITEAOF三个命令的方式和平时有所不同,主要有一下几种情形:
- 在BGSAVE命令执行期间,客户端发起的SAVE命令会被服务器拒绝。服务器禁止SAVE命令和BGDAVE命令同时执行是为了避免父进程和子进程同时执行两个rdbsave调用,防止产生竞争条件。
- 在BGSAVE命令执行期间客户端发起BGSAVE命令会被服务器拒绝,因为同时执行两个BGSAVE命令也会产生竞争条件
- 在BGSAVE命令执行期间,客户端发起BGREWRITEAOF命令会被延迟到BGSAVE命令执行完毕之后执行
- 在BGREWRITEAOF命令执行期间,客户端发起BGSAVE命令会被服务器直接拒绝,主要是因为BGREWRITEAOF和BGSAVE命令都是由子进程来完成的,禁止他们同时执行知识一个性能方面的考虑,因为并发出两个子进程,并且这两个子进程同时进行大量的磁盘写入操作,并不是什么好事情。
save和bgsave的区别
命令 | save | bgsave |
---|---|---|
IO阻塞 | 同步 | 异步 |
阻塞? | 是 | 是(阻塞发生在fork) |
复杂度 | O(n) | O(n) |
优点 | 不会消耗额外内存 | 不阻塞客户端命令 |
缺点 | 阻塞客户端命令 | 需要fork,消耗内存 |
RDB文件的载入
对于RDB文件的载入就相对简单了,RDB文件的载入工作是在服务器启动的时候自动执行的,并且在服务器载入RDB文件期间,会一直处于阻塞状态,直到载入工作完成为止。
RDB自动间歇性保存
上面我们讲了如何创建RDB文件,但是什么时候来创建RBD文件呢?除了手动的执行命令之外,我们还可以通过配置来让服务器自动来创建RDB文件,我们可以在redis的配置文件中通过以下配置来实现:
# 快照配置
# 注释掉“save”这一行配置项就可以让保存数据库功能失效
# 设置sedis进行数据库镜像的频率。
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 900 1
save 300 10
save 60 10000
#当RDB持久化出现错误后,是否依然进行继续进行工作,yes:不能进行工作,no:可以继续进行工作,可以通过info中的rdb_last_bgsave_status了解RDB持久化是否有错误
stop-writes-on-bgsave-error yes
#使用压缩rdb文件,rdb文件压缩使用LZF压缩算法,yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间
rdbcompression yes
#是否校验rdb文件。从rdb格式的第五个版本开始,在rdb文件的末尾会带上CRC64的校验和。这跟有利于文件的容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗,所以如果你追求高性能,可以关闭该配置。
rdbchecksum yes
#rdb文件的名称
dbfilename dump.rdb
################################# REPLICATION #################################
#复制选项,slave复制对应的master。
# slaveof
#如果master设置了requirepass,那么slave要连上master,需要有master的密码才行。masterauth就是用来配置master的密码,这样可以在连上master后进行认证。
# masterauth
#当从库同主机失去连接或者复制正在进行,从机库有两种运行方式:1) 如果slave-serve-stale-data设置为yes(默认设置),从库会继续响应客户端的请求。2) 如果slave-serve-stale-data设置为no,除去INFO和SLAVOF命令之外的任何请求都会返回一个错误”SYNC with master in progress”。
slave-serve-stale-data yes
#作为从服务器,默认情况下是只读的(yes),可以修改成NO,用于写(不建议)。
slave-read-only yes
用户可以通过save选项设置多个保存条件(当然可以超过三个了,十个八个都可以,因为这些保存条件其实是保存在一个数组中的),但是只要其实任意一个条件满足,服务器就会执行BGSAVE命令。而上面的三条保存配置其实也是在我们开启了RDB持久化但是没有配置相关保存条件下时服务器给的默认的配置。
RDB自动保存的触发原理
我们知道了如何配置保存条件的,但是这个保存条件是怎么触发的呢?
其实服务器除了通过一个数据存储我们的保存条件外,还会维持一个dirty的计数器,以及一个lastsave属性,其中dirty计数器用来统计距离上一次成功执行SAVE命令或者BGSAVE命令后服务器对数据库状态进行了多少次修改(包括写入,删除,更新等操作),而lastsave属性是一个UNIX的时间戳,记录了服务器上一次成功执行SAVE命令或者BGSAVE命令的时间。这两个值都会在成功执行完BGSAVE命令后重置:dirty重置成0,lastsave更新为当前时间。
而对于条件的判断则是Redis服务器会周期性的操作函数serverCron默认每隔100毫秒执行一次,该函数用于对正在运行的服务器进行维护,它的其中一项工作就是检查save选项所设置的保存条件是否满足,如果满足则执行BGSAVe命令。必须同时满足的条件是:1.距离上一次成功执行保存的时间超过设置的时间,2.数据库状态的修改次数超过设置的修改次数
RDB总结
- RDB是Redis内存到硬盘的快照,用于持久化
- save通常会阻塞Redis
- bgsave不会阻塞Redis,但是会fork新进程
- save自动配置满足任一就会被执行
- 有些触发机制不容忽视
AOF持久化的逻辑
与RBD持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存redis服务器所执行的写命令来记录数据库状态的。
被写入到AOF文件的命令都是以Redis的命令请求协议格式保存的,因为Redis的命令请求协议是纯文本的,所以我们可以直接打开一个AOF文件,里面保存的基本都是我们执行的命令,但是会有一些SELECT命令,SELECT命令是用于指定数据库的,此命令是服务器自动添加的。
AOF持久化的过程
AOF持久化过程的实现可以分为命令追加、文件写入、文件同步三个步骤。
命令追加:当AOF功能打开的时候,服务器执行一条命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的额末尾,但是aof_buf缓冲区的命令什么时候写入aof文件这个要根据额我们的appendfsync的相关配置来决定
文件写入和同步:为了提高文件的写入效率,在现代的操作系统中,当用户调用write函数,将一些数据写入到文件的时候,操作系统通常会将写入数据暂时存放到一个内存缓冲区里面,等到缓冲区满了或者超过了执行的时限之后,才真正的将缓冲区中的数据写入到磁盘里面
我们需要注意的是,Redis的服务器进程就是一个事件循环,这个循环中的文件时间负责接收客户端的命令请求,以及向客户端发送命令回复,而时间时间则负责执行向serverCron函数这样的需要定时运行的函数
因为服务器在处理文件事件时可能会执行写命令,使得这些内容被追加到aof_buf缓冲区里面,所以在服务器每次结束一个时间循环之前,它都会调用flushAppendOnlyFile函数,考虑是否需要将aof_buf缓冲区中的内容写入和保存到AOF文件里面。
flushAppendOnlyFile函数的行为是有服务器配置文件的appendfsync选项的值来决定的,appendfsync的值有三种:
-
always:将aof_buf缓冲区中的数据写入并同步到AOF文件。这种设置时最安全的,就变机器出现故障,也只是会丢失一个事件循环中所产生的命令数据
-
everysec:将aof_buf缓冲区的所有内容写入到AOF文件,如果上次同步AOF文件的时间距离现在超过1s,那么再次对AOF文件进行同步,并且这个同步操作有一个线程专门完成。这种设置,服务器在每个事件循环中都要将aof_buf缓冲区中的所有内容写入到Aof文件,并且每隔1s就要在子线程中对AOF文件进行一次同步。从效率上来讲everysec模式足够快,并且就算出现故障停机,数据库也只丢失一秒钟的命令数据。
no:在服务器的每个事件循环钟都要将aof缓冲区中的所有内容写入到AOF文件,但不对AOF文件进行同步,何时同步有操作系统来同步,所以这种设置是如果服务器停机,将会丢掉上次AOF文件同步后的所有写命令数据。
命令 | always | everysec | no |
---|---|---|---|
优点 | 不丢失数据 | 每一秒fsync丢一秒数据 | 不用管 |
缺点 | IO开销较大,一般的stata盘只有几百TPS | 丢一秒数据 | 不可控 |
AOF文件的载入和数据还原
因为AOF文件中包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一边AOF文件里面保存的写命令,就可以还原服务器关闭之前的数据状态。
AOF重写
因为Aof持久化是通过保存被执行的写命令来记录数据库状态的,所以随着服务器事件的流逝,AOF文件内容会越来越大,文件体积也会也来越大,如果不加控制体积过大的AOF文件很可能对Redis服务器甚至宿主机造成影响。对于数据恢复来说,AOF文件中会有很多冗余的命令,比如对某个key操作了八次,AOF文件中会记录八条写命令,但是我们关心的只是最后一次命令执行后的数据状态,为了解决AOF文件体积膨胀问题,Redis提供了AOF重写功能。
AOF重写没有对进行AOF任何操作,只是对AOF重写时候的数据状态转成写命令保存起来,也就是说重写后的AOF文件中保存着能够换肤重写时数据库状态的最小的写命令数,这样就可以大大的减少文件的体积了。
需要注意的是在重写程序中在处理列表、哈希表、集合、有序集合这四种可能会带来多个元素的键时,会先检查包含元素的数量,如果数量超过了设置的常量值(这个值不通版本是不一样的),就会用多条命令来记录这个键而不单单时一条命令。
因为Reids是单线程的,为了不是AOF重写时阻塞服务器的正常运行,redis决定将AOF重写放到一个子进程中进行,这样做有两个好处:
- 子进程进行AOF重写期间,服务器进程(父进程)可以继续处理命令请求
- 子进程带有服务器进程的数据副本,使用子进程而不是线程,可以在避免使用所得情况下,保证数据得安全性
有个问题我们要注意,在子进程AOF重写期间,服务器进程还需要继续处理命令请求,这个时候redis用来一个AOF重写缓冲区来解决这个问题,也就是说,在子进程AOF重写期间,服务器进程会进行三个工作:
- 执行客户端发来得命令
- 将执行后得写命令追加得AOF缓冲区
- 将执行后得写命令追加得AOF重写缓冲区
这样一来可以保证:
- AOF缓冲区得内容会定期被写入和同步到AOF文件,对现在AOF文件得处理工作会如常进行
-
从创建子进程开始,服务器得所有写命令都会被记录到AOF重写缓冲区里面
当子进程完成AOF重写后,会向服务器进程发一个信号,并调用一个信号处理函数执行一下工作:
- 将Aof重写缓冲区中得所有内容写入到新得Aof文件中,这是新AOF文件所保存得数据库状态将和服务器当前得数据库状态保持一致
- 对新的AOF文件进行改名,原子的覆盖现在的AOF文件,完成新旧文件的交替
在整个AOF后台重写过程中,只有信号处理的时候会对服务器进程(父进程)造成阻塞,其他时候AOF后台重写都不会阻塞父进程,这将AOF重写对服务器造成的影响讲到了最低。
RDB持久化和AOF持久化的对比
RDB优劣势
优势:
RDB只代表某个时间点上的数据快照,所以适用于备份与全量复制,如一天进行备份一次。
Redis再加载RDB文件恢复数据远快于AOF文件
性能上考虑RDB优于AOF,因为我们保存RDB文件只需fork一次子进程进行保存操作,父进程没有对磁盘I/O
劣势:
RDB没办法做到实时的持久化数据,因为fork是重量级别的操作,频繁执行成本过高
RDB需要经常fork子进程来保存数据集到磁盘,当数据集比较大额时候,fork的过程是比较耗时的,可能会导致redis在一些毫秒级不能响应客服端请求
老版本的Redis无法兼容新版本的RDB文件
AOF优劣势
优势:
通过配置同步策略基本能够达到实时持久化数据,如配置为everysec,则每秒同步一次AOF文件,也就是说最多丢失一秒钟的数据,兼顾了性能与数据的安全性
AOF 文件是一个只进行追加操作的日志文件(append only log), 因此对 AOF 文件的写入不需要进行 seek , 即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等等), redis-check-aof 工具也可以轻易地修复这种问题。
劣势:
对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
与AOF相比,在恢复大的数据时候,RDB方式更快一些
RDB和AOF
命令 | RDB | AOF |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢数据 | 根据策略决定 |
轻重 | 重 | 轻 |
Redis 4.0 混合持久化
重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。 Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。AOF在重写(aof文件里可能有太多没用指令,所以aof会定期根据内存的最新数据生成aof文件)时将重写这一刻之前的内存rdb快照文件的内容和增量的 AOF修改内存数据的命令日志文件存在一起,都写入新的aof文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,原子的覆盖原有的AOF文件,完成新旧两个AOF文件的替换;
AOF根据配置规则在后台自动重写,也可以人为执行命令bgrewriteaof重写AOF。 于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。
开启混合持久化:
aof-use-rdb-preamble yes
混合持久化aof文件结构
特别感谢:
Redis之RDB和AOF持久化介绍