Redis系列四---告别“闪失”,引爆Redis的持久能力

Redis数据持久化

       Redis是一个内存数据库,所以其运行效率非常高。但也存在一个问题:内存中的数据是不持久的,若主机宕机或Redis关机重启,则内存中的数据全部丢失。当然,这是不允许的,为了保证Redis数据不丢失,那就要把数据从内存存储到硬盘上,以便在服务重启时还能够从磁盘中恢复原有数据,这就是Redis的数据持久化。

Redis数据持久化有三种方式:

  • RDB快照(Redis DataBase):将某一时刻的内存数据以二进制的方式写入磁盘(早期默认方式)
  • AOF日志(Append Only File):文件追加方式,记录所有的操作命令,并以文本的形式追加到文件中
  • 混合持久化方式:Redis4.0新增了混合持久化的方式,集成了RDB和AOF的优点

1.1、持久化基本原理

      Redis 持久化也称为钝化,是指将内存中数据库的状态描述信息保存到磁盘中。只不过是不同的持久化技术对数据的状态描述信息是不同的,生成的持久化文件也是不同的。但它们的作用都是相同的:避免数据意外丢失

       通过手动方式或自动定时方式,或自动条件触发方式,将内存中数据库的状态描述信息写入到指定的持久化文件中。当系统重新启动时,自动加载持久化文件,并根据文件中数据库状态描述信息将数据恢复到内存中,这个数据恢复过程也称为激活。这个钝化与激活的过程就是Redis持久化的基本原理。

      不过从以上分析可知,对于Redis单机状态下,无论是手动方式还是定时方式或条件触发方式,都存在数据丢失问题:在尚未手动或自动保存时发生了Redis宕机状况,那么从上次保存到宕机期间产生的数据就会丢失。不同的持久化方式,其数据的丢失率也是不同的。

1.2、RDB机制

     RDB其实就是把数据以快照的形式保存在磁盘上。

     RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是早期默认的持久化方式,这种方式就是将内存中的数据以快照的方式写入二进制文件中,默认的文件名为dump.rdb

     RDB采用内存快照方式,它记录的是某一刻的数据,而不是操作,所以采用RDB的方式做数据恢复时只需把RDB文件读入内存即可,实现快速恢复。

    既然RDB机制是通过把某个时刻的所有数据生成一个快照保存下来,那么就应该有一个触发机制来实现这个过程,对于RDB来说提供来三种机制:save、BGSAVE和自动化。

1.2.1、save触发方式

     save命令会阻塞当前Redis服务,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。具体流程如下:

      执行完成时如果存在老的dump.rdb文件,就用新的替换掉旧的。整个过程Redis不能处理客户端的读写请求,这种方式显然不太友好。

1.2.2、BGSAVE触发方式

       执行BGSAVE命令时,Redis会在后台异步进程快照操作,同时还可以响应客户端的请求,具体流程如下:

      具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束,阻塞只发生在fork阶段,一般时间很短。基本上Redis内部所有的RDB操作都是采用BGSAVE命令的这个过程。

       在执行快照的同时,Redis会借助操作系统提供的写时复制技术(Copy-On-Write,COW),正常处理写操作。BGSAVE子进程由主进程fork生成,可以共享主进程的所有内存数据,BGSAVE子进程运行后,开始读取主进程的内存数据,并把它们写入RDB文件。

Redis是怎么解决在BGSAVE做快照时允许数据修改的呢?

这里主要是利用BGSAVE的子进程实现的,具体操作如下:

如果主进程执行读操作,则主进程和子进程互相不影响;

        如果主进程执行写操作,则被修改的数据会复制一份副本,然后,主进程在这个数据副本上进行修改,同时BGSAVE子进程可以继续把原来的数据写入RDB文件。

虽然BGSAVE执行时不阻塞主进程,但是,如果频繁地执行全量快照也会带来两方面的开销:

  • 一方面,频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个写操作竞争有限的磁盘IO,前一个快照还没写完,后一个又开始写,容易造成恶性循环,所以在Redis中如果有一个BGSAVE在执行,就不会启动第二个BGSAVE子进程。
  • 另一方面,BGSAVE子进程需要通过fork主进程创建,fork操作本身会阻塞主进程,而且主进程的内存越大,阻塞时间就越长。

1.2.3、自动触发

      自动触发是由配置文件来控制的。在redis.conf配置文件中,里面有如下配置,我们可以去设置:

  • save:这里是用来配置触发 Redis的 RDB 持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如save m n。表示m秒内数据集存在n次修改时,自动触发bgsave。
    默认如下配置:
    表示900 秒内如果至少有 1个key的值变化,则保存save 900 1
    表示300 秒内如果至少有 10个key 的值变化,则保存save 300 10
    表示60 秒内如果至少有 10000个key 的值变化,则保存save 60 10000
    不需要持久化,那么你可以注释掉所有的 save行来停用保存功能。
  • stop-writes-on-bgsave-error :默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了
  • rdbcompression :默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。
  • rdbchecksum :默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
  • dbfilename :设置快照的文件名,默认是 dump.rdb
  • dir:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名

       RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑。当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。

1.3、AOF机制

       全量备份总是耗时的,有时候我们提供一种更加高效的方式AOF,工作机制很简单,Redis会将每一个收到的写命令都通过write函数追加到文件中。通俗的理解就是日志记录。

      AOF采用的是写后日志的方式,Redis先执行命令把数据写入内存,然后再记录日志到文件中。AOF日志记录的是操作命令,不是实际的数据,如果采用AOF方法做故障恢复时需要将全量日志都执行一遍

1.3.1、AOF三种触发机制

  • 每修改同步always:每个写命令执行完,立马同步地将日志写回磁盘,同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好
  • 每秒同步everysec:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘,异步操作,每秒记录,如果一秒内宕机,有数据丢失,
  • 不同步no:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘

配置项

写回时机

优点

缺点

Always

同步写回

可靠性高,数据基本不丢失

每个写命令都有落盘,性能低

Everysec

每秒写回

性能适中

宕机时丢失1秒内的数据

No

操作系统控制写回

性能好

宕机时丢失数据较多

        需要根据业务场景选择,想要高性能,数据完整性不很重要就选No,数据完整性很重要就选Always,允许数据有一点丢失,还希望性能不受太大影响,就选Everysec。

1.3.2、AOF的文件重写

       AOF 是以文件的形式在记录接收到的所有写命令。随着接收的写命令越来越多,AOF 文件会越来越大。这也就意味着,我们一定要小心 AOF 文件过大带来的性能问题,主要在于以下三个方面:

  • 文件系统本身对文件大小有限制,无法保存过大的文件;
  • 如果文件太大,之后再往里面追加命令记录的话,效率也会变低;
  • 如果发生宕机,AOF 中记录的命令要一个个被重新执行,用于故障恢复,如果日志文件太大,整个恢复过程就会非常缓慢,这就会影响到 Redis 的正常使用。

      为了解决AOF文件体积膨胀的问题,Redis提供了AOF文件重写机制,AOF 重写机制的工作原理为Redis 根据数据库的现状创建一个新的 AOF 文件,新旧两个文件所保存的数据库状态是相同的,但是新的AOF文件不会包含任何浪费空间的冗余命令,通常体积会较旧AOF文件小很多。重写机制具有多变一功能。所谓的多变一,也就是说,旧日志文件中的多条命令有可能修改了同一个键的值,在重写后的新日志中变成了一条命令(重写后只记录最后键值对的修改命令)。

重写的过程:

     重写过程是由后台子进程 bgrewriteaof 来完成的,这也是为了避免阻塞主进程 ,导致数据库性能下降。
      可以把重写的过程总结为一个拷贝,两处日志 :

  • 一个拷贝:就是指每次执行重写时,主进程 fork 出后台的 bgrewriteaof 子进程。此时,fork 会把主进程的内存拷贝一份给 bgrewriteaof 子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof 子进程就可以在不影响主进程的情况下,逐一把拷贝的数据写成操作,记入重写日志。
  • 第一处日志:因为主进程未阻塞,仍然可以处理新来的操作,Redis会把这个操作写到它的AOF缓冲区,即使宕机了,这个 AOF 日志的操作仍然是齐全的,可以用于恢复。
  • 第二处日志:从bgrewriteaof 子进程创建时到AOF重写完成期间的操作也会被写到重写日志的缓冲区,这样重写日志也不会丢失新的操作,等到拷贝数据的所有操作记录重写完成后,重写日志缓冲区记录的这些新操作也会写入新的 AOF 文件,以保证数据库最新状态的记录。

      此时,我们就可以用新的 AOF 文件替代旧文件了,完成AOF文件瘦身到目的。

     设置重写触发策略,例如配置auto-aof-rewrite-percentage和auto-aof-rewrite-min-size,让Redis自动在文件增长到一定比例或超过最小尺寸时触发AOF重写。

       AOF增量同步:配置aof-rewrite-incremental-fsync选项,可以控制在AOF重写过程中增量地进行fsync操作,从而减少AOF文件过大时的同步操作开销。

1.4、RDB和AOF对比

       RDB快照是一次全量备份,AOF基本上实时备份数据库的每一步操作,所以两种备份的方式决定了这两种备份的特性和使用场景,如下表格列举两种工作机制的优缺点。

备份方式

优点

缺点

使用场景

RDB

RDB文件紧凑,全量备份

在进程快照持久化时主进程修改的数据容易丢失

定时全量备份

AOF

可以更好的保护数据不丢失,一般配置最多丢失1秒的数据;

AOF日志文件通过可读的方式记录操作命令,容易通过AOF日志文件恢复无操作;

AOF日志支持重写,对日志文件瘦身

对同一份数据来说,AOF日志比RDB文件更大;

通过AOF恢复数据比RDB更慢;

实时记录数据操作命令,对误操作的紧急恢复;

结合RDB对全量备份实时备份操作记录

1.5、RDB和AOF混合使用

      RDB和AOF应该怎么选择,需要看自己的需求,最好的方式是这两种备份结合使用,如下图是两种备份的具体对比。

项目

RDB

AOF

加载优先级

日志文件体积

数据恢复速度

数据安全性

容易丢数据

根据策略决定

轻重

       Redis执行RDB备份的执行频率非常重要,因为RDB太频繁会频繁写硬盘影响磁盘性能,极端情况下可能造成硬盘夯死导致系统宕机,RDB时间间隔太长就容易丢失数据,在Redis4.0版本后支持了RDB和AOF结合使用,就是RDB全量备份按一定频率执行,在两次RDB之间使用AOF日志记录期间的所有命令操作,这样一来RDB快照不用执行太频繁,也就避免了频繁fork对主进程对影响,而AOF只记录两次RDB备份之间的操作,不需记录所有的操作,因此AOF日志文件也不会出现很大的情况,也可以避免了AOF日志重写的开销。

特别提醒:     

       混合使用RDB和AOF综合了两者的优势,避免了两者的劣势,但配置结合使用时需要注意,因为结合使用加载日志文件时AOF的优先级比RDB的优先级高,所以在默认配置开启RDB的情况下开启AOF时,不能直接修改配置文件redis.conf然后直接重启redis服务,这样重启时很有可能AOF日志文件还没生成,重启Redis服务后自动加载AOF文件(空文件),就清空了Redis的数据,这时再执行一次RDB备份,就彻底清空了Redis数据,在生产中这种情况是灾难性的。所以开启AOF时需要先通过命令redis-cli config set appendonly yes 临时开启AOF,让Redis先执行一次AOF日志生成,保证AOF日志文件记录了数据库的最新状态后,才能修改配置文件redis.conf重启Redis服务使其永久有效。


欢迎关注作者的公众号,公众号每天分享运维干货文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值