Redis持久化的取舍和选择

什么是持久化

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命令执行时的服务器状态


Redis持久化的取舍和选择_第1张图片
image.png

  其实这个很简单,我们上面已经提到过SAVE命令或阻塞一切请求,当然拥抱口BGSAVE请求了,所以在执行SAVE期间,再次执行BGSAVE命令会被直接拒绝。

BGSAVE命令执行时的服务器状态


Redis持久化的取舍和选择_第2张图片
image.png

我们知道BGSAVE命令是服务器的子进程来完成的,服务器进程可以继续接收和执行命令,但是再BGSAVE命令执行期间服务器处理SAVE、BGSAVE、BGREWRITEAOF三个命令的方式和平时有所不同,主要有一下几种情形:

  1. 在BGSAVE命令执行期间,客户端发起的SAVE命令会被服务器拒绝。服务器禁止SAVE命令和BGDAVE命令同时执行是为了避免父进程和子进程同时执行两个rdbsave调用,防止产生竞争条件。
  2. 在BGSAVE命令执行期间客户端发起BGSAVE命令会被服务器拒绝,因为同时执行两个BGSAVE命令也会产生竞争条件
  3. 在BGSAVE命令执行期间,客户端发起BGREWRITEAOF命令会被延迟到BGSAVE命令执行完毕之后执行
  4. 在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总结

  1. RDB是Redis内存到硬盘的快照,用于持久化
  2. save通常会阻塞Redis
  3. bgsave不会阻塞Redis,但是会fork新进程
  4. save自动配置满足任一就会被执行
  5. 有些触发机制不容忽视

AOF持久化的逻辑

与RBD持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存redis服务器所执行的写命令来记录数据库状态的。

被写入到AOF文件的命令都是以Redis的命令请求协议格式保存的,因为Redis的命令请求协议是纯文本的,所以我们可以直接打开一个AOF文件,里面保存的基本都是我们执行的命令,但是会有一些SELECT命令,SELECT命令是用于指定数据库的,此命令是服务器自动添加的。

AOF持久化的过程

Redis持久化的取舍和选择_第3张图片
AOF运行原理-创建
Redis持久化的取舍和选择_第4张图片
AOF运行原理-恢复

AOF持久化过程的实现可以分为命令追加、文件写入、文件同步三个步骤。

命令追加:当AOF功能打开的时候,服务器执行一条命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的额末尾,但是aof_buf缓冲区的命令什么时候写入aof文件这个要根据额我们的appendfsync的相关配置来决定

文件写入和同步:为了提高文件的写入效率,在现代的操作系统中,当用户调用write函数,将一些数据写入到文件的时候,操作系统通常会将写入数据暂时存放到一个内存缓冲区里面,等到缓冲区满了或者超过了执行的时限之后,才真正的将缓冲区中的数据写入到磁盘里面

我们需要注意的是,Redis的服务器进程就是一个事件循环,这个循环中的文件时间负责接收客户端的命令请求,以及向客户端发送命令回复,而时间时间则负责执行向serverCron函数这样的需要定时运行的函数

因为服务器在处理文件事件时可能会执行写命令,使得这些内容被追加到aof_buf缓冲区里面,所以在服务器每次结束一个时间循环之前,它都会调用flushAppendOnlyFile函数,考虑是否需要将aof_buf缓冲区中的内容写入和保存到AOF文件里面。

flushAppendOnlyFile函数的行为是有服务器配置文件的appendfsync选项的值来决定的,appendfsync的值有三种:


Redis持久化的取舍和选择_第5张图片
image.png
  1. always:将aof_buf缓冲区中的数据写入并同步到AOF文件。这种设置时最安全的,就变机器出现故障,也只是会丢失一个事件循环中所产生的命令数据


    Redis持久化的取舍和选择_第6张图片
    image.png
  2. everysec:将aof_buf缓冲区的所有内容写入到AOF文件,如果上次同步AOF文件的时间距离现在超过1s,那么再次对AOF文件进行同步,并且这个同步操作有一个线程专门完成。这种设置,服务器在每个事件循环中都要将aof_buf缓冲区中的所有内容写入到Aof文件,并且每隔1s就要在子线程中对AOF文件进行一次同步。从效率上来讲everysec模式足够快,并且就算出现故障停机,数据库也只丢失一秒钟的命令数据。


    Redis持久化的取舍和选择_第7张图片
    image.png
  3. 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重写放到一个子进程中进行,这样做有两个好处:

  1. 子进程进行AOF重写期间,服务器进程(父进程)可以继续处理命令请求
  2. 子进程带有服务器进程的数据副本,使用子进程而不是线程,可以在避免使用所得情况下,保证数据得安全性

有个问题我们要注意,在子进程AOF重写期间,服务器进程还需要继续处理命令请求,这个时候redis用来一个AOF重写缓冲区来解决这个问题,也就是说,在子进程AOF重写期间,服务器进程会进行三个工作:

  1. 执行客户端发来得命令
  2. 将执行后得写命令追加得AOF缓冲区
  3. 将执行后得写命令追加得AOF重写缓冲区

这样一来可以保证:

  1. AOF缓冲区得内容会定期被写入和同步到AOF文件,对现在AOF文件得处理工作会如常进行
  2. 从创建子进程开始,服务器得所有写命令都会被记录到AOF重写缓冲区里面


    Redis持久化的取舍和选择_第8张图片
    image.png

当子进程完成AOF重写后,会向服务器进程发一个信号,并调用一个信号处理函数执行一下工作:

  1. 将Aof重写缓冲区中得所有内容写入到新得Aof文件中,这是新AOF文件所保存得数据库状态将和服务器当前得数据库状态保持一致
  2. 对新的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持久化的取舍和选择_第9张图片
image.png
特别感谢:

Redis之RDB和AOF持久化介绍

你可能感兴趣的:(Redis持久化的取舍和选择)