Redis数据安全与性能保障

Redis持久化


RDB(快照持久化)

在这里插入图片描述

  • RDB文件的载入一般情况是自动的,redis服务器启动的时候如果检测到RDB文件的存在,那么redis会自动载入这个文件。
    在这里插入图片描述

  • RDB保存的文件名:dump.rdb(二进制文件)

  • 快照持久化是不可靠的,如果出问题,那么Redis将丢失最近一次创建快照之后写入的所有数据,会自动读取dump.rdb文件中的数据

redis.conf 配置

# 触发自动保存快照
# save <seconds> <changes>
save 900 1    # 900 秒内有至少有 1 个键被改动
save 300 10   # 300 秒内有至少有 10 个键被改动
save 60 10000 # 60 秒内有至少有 1000 个键被改动


# 设置在保存快照出错时,是否停止redis命令的写入
stop-writes-on-bgsave-error yes

# 是否在导出.rdb数据库文件的时候采用LZF压缩
rdbcompression yes

#  是否开启CRC64校验
rdbchecksum yes

# 导出数据库的文件名称
dbfilename dump.rdb

# 导出的数据库所在的目录
dir ./

触发机制

满足save规则的情况下,会自动触发RDB

  • 执行flushall命令
  • 退出redis
  • 执行BGSAVE、SAVE(不经常用,需要等待持久化完毕)
  • SYNC复制

优点:

  • RDB是一个非常紧凑(有压缩)的文件,它保存了某个时间点的数据,非常适用于数据的备份。

  • RDB作为一个非常紧凑(有压缩)的文件,可以很方便传送到另一个远端数据中心 ,非常适用于灾难恢复.

  • RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,
    父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.

  • 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.

缺点:

  • RDB容易造成数据的丢失。假设每5分钟保存一次快照,如果Redis因为某些原因不能正常工作,
    那么从上次产生快照到Redis出现问题这段时间的数据就会丢失了。

  • RDB使用fork()产生子进程进行数据的持久化,如果数据比较大的话可能就会花费点时间,造成Redis停止服务几毫秒。如果数据量很大且CPU性能不是很好的时候,停止服务的时间甚至会到1秒。

Redis进程每占用1GB内存,fork()产生子进程所需的时间就要增加200ms~300ms

AOF

相对于RDB的不可靠,AOF则提供了一种类似于数据库binlog的方式 ,更为可靠。

简单来说AOF持久化会将被执行的命令写到AOF文件的末尾,所以Redis只要从头到尾执行一次AOF文件包含的所有写命令,就可以恢复数据集了。但对于大数据环境就比较慢了。

AOF保存的文件名: appendonly.aof

//将conf文件中的配置选项配置为yes
appendonly yes   

//在客户端输入如下命令也可,但是Redis服务器重启后会失效
127.0.0.1:6379> config set appendonly yes
OK

关于文件的写入和同步的资料

因为为了提高文件的写入效率,在现代操作系统中,当用户调用write函数,将一些数据写入到文件的时候,os通常会将写入数据暂时保存在一个内存缓冲区里面(例如,unix系统实现在内核中设有缓冲区高速缓存或页高速缓存,当我们向文件写入数据时,内核通常先将数据复制到缓冲区中,然后排入队列,晚些时候再写入磁盘),这种方式称为延迟写,等到缓冲区的空间被填满,或者超过了指定的时限,或者内核需要重用缓冲区存放其它磁盘块数据时,才会真正将缓冲区中的所有数据写入到磁盘里面。
文件写入:只是写入到了内存缓冲区,可能还没有写到文件所拥有的磁盘数据块上 文件同步:将缓冲区中的内容冲洗到磁盘上

为了兼顾性能和数据安全,可以使用 appendfsync everysec
选项,让Redis以每秒一次的频率对AOF文件进行同步。Redis同步一次AOF时的性能和不使用任何持久化特性时的性能相差无几。即使出现数据丢失也只丢失1s的
appendfsync no
选项则是让操作系统决定什么时候进行AOF文件同步,一般情况下对Redis的性能无影响,但是一旦服务器宕机,就会丢失不定量的数据。如果用户的磁盘处理写入操作的速度不够快的话,缓冲区被等待写入硬盘的数据填满的时,Redis的写入操作就会阻塞,并导致Redis处理命令请求的速度变慢。

重写/压缩 AOF文件

  1. 随着Redis的不断运行,AOF文件体积也会越来越大,极端情况下可能用完服务器的存储空间
  2. Redis重启后 需要通过重新载入AOF文件记录来还原数据集,如果AOF文件过大,还原的时间将会非常长。
  3. 为了解决这个问题 —> Redis用户可以执行 BGREWRITEAOF 命令,将会通过移除AOF文件中冗余命令来重写AOF文件,使AOF文件尽可能的小。
127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started

BGREWRITEAOF的工作原理和BGSAVE非常类似,存在的问题也是相似的
AOF可以通过设置auto-aof-rewrite-percentageauto-aof-rewrite-min-size选项来自动执行BGREWRITEAOF 参数如下

auto-aof-rewrite-percentage 100 #当前AOF文件大小和上一次重写时AOF文件大小的比值
auto-aof-rewrite-min-size 64mb  #文件的最小体积

载入与数据还原

读取AOF文件并还原数据库的步骤如下

  1. 创建一个不带网络连接的伪客户端
  2. 从AOF文件中分析并读取出一条写命令
  3. 使用伪客户端执行被读出的写命令
    在这里插入图片描述
    一直执行步骤2、3,直到AOF文件中的所有写命令都被处理完毕为止

这时可能会出现一个问题。 服务器可能在程序正在对 AOF 文件进行写入时停机,造成了 AOF 文件出错,那么 Redis 在重启时会拒绝载入这个 AOF 文件,从而确保数据的一致性不会被破坏 当发生这种情况时, 可以用以下方法来修复出错的 AOF 文件:

  • 为现有的 AOF 文件创建一个备份。
  • 使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复: redis-check-aof –fix
  • (可选)使用 diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,查看两个文件之间的不同之处。
  • 重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。

另外redis.conf配置文件中还提供了一个参数来控制是否忽略最后一条可能存在问题的指令,如下

aof-load-truncated yes

优点:

  • 比RDB可靠。可以指定不同的fsync策略: always、everysec、no

  • AOF日志文件是一个纯追加的文件,就算是突然断电也不会出现日志的定位或者损坏的问题。

  • 当AOF文件太大时,Redis可以配置后台重写,重写很安全,类似于RDB一样会在一个新文件上进行,同时Redis会继续往旧的文件追加数据。新文件上会写入能重建当前数据集的最小操作命令的集合。
    当新文件重写完,Redis会把新旧文件进行切换,然后开始把数据写到新文件上。

  • AOF把操作命令以简单易懂的格式一条接一条的保存在文件里,很容易导出来用于恢复数据。
    例如我们不小心用FLUSHALL命令把所有数据刷掉了,只要文件没有被重写,我们可以把服务停掉,把最后那条命令删掉,然后重启服务,这样就能把被刷掉的数据恢复回来。

缺点:

  • 在相同的数据集下,AOF文件的大小一般会比RDB文件大。

  • 在某些fsync策略下,AOF的速度会比RDB慢。通常fsync设置为每秒一次就能获得比较高的性能,而在禁止fsync的情况下速度可以达到RDB的水平。

  • 在过去 曾经发现一些很罕见的BUG导致使用AOF重建的数据跟原数据不一致的问题。

方案选择与常见问题


持久化策略选择

要明白无论是RDB还是AOF,持久化的开启都是要付出性能方面代价的:
对于RDB持久化,一方面是BGSAVE在进行fork操作时Redis主进程会阻塞,另一方面,子进程向硬盘写数据也会带来IO压力;

对于AOF持久化,向硬盘写数据的频率大大提高(everysec策略下为秒级),IO压力更大,甚至可能造成AOF追加阻塞问题(后面会详细介绍这种阻塞);

此外,AOF文件的重写与RDB的BGSAVE类似,会有fork时的阻塞和子进程的IO压力问题。

相对来说,由于AOF向硬盘中写数据的频率更高,因此对Redis主进程性能的影响会更大。

Redis恢复数据时会先检查AOF文件是否存在,如果不存在就尝试加载RDB文件。

在实际生产环境中,根据数据量、应用对数据的安全要求、预算限制等不同情况,会有各种各样的持久化策略。如完全不使用任何持久化、使用RDB或AOF的一种,或同时开启RDB和AOF持久化等。此外,持久化的选择必须与Redis的主从策略一起考虑,因为主从复制与持久化同样具有数据备份的功能,而且主机master和从机slave可以独立的选择持久化方案。

根据场景来分析

(1)如果Redis中的数据完全丢弃也没有关系(如Redis完全用作DB层数据的cache),那么无论是单机,还是主从架构,都可以不进行任何持久化。

(2)在单机环境下(对于个人开发者,这种情况可能比较常见),如果可以接受十几分钟或更多的数据丢失,选择RDB对Redis的性能更加有利;如果只能接受秒级别的数据丢失,应该选择AOF。

(3)但在多数情况下,我们都会配置主从环境,slave的存在既可以实现数据的热备,也可以进行读写分离分担Redis读请求,以及在master宕掉后继续提供服务。在这种情况下,一种可行的做法是:
master:完全关闭持久化(包括RDB和AOF),这样可以让master的性能达到最好
slave:关闭RDB,开启AOF(如果对数据安全要求不高,开启RDB关闭AOF也可以),并定时对持久化文件进行备份(如备份到其他文件夹,并标记好备份的时间);然后关闭AOF的自动重写,然后添加定时任务,在每天Redis闲时(如凌晨12点)调用bgrewriteaof。

为什么开启了主从复制,可以实现数据的热备份,还需要设置持久化呢?

因为在一些特殊情况下,主从复制仍然不足以保证数据的安全,例如:

master和slave进程同时停止:考虑这样一种场景,如果master和slave在同一栋大楼或同一个机房,则一次停电事故就可能导致master和slave机器同时关机,Redis进程停止;如果没有持久化,则面临的是数据的完全丢失。
master误重启:考虑这样一种场景,master服务因为故障宕掉了,如果系统中有自动拉起机制(即检测到服务停止后重启该服务)将master自动重启,由于没有持久化文件,那么master重启后数据是空的,slave同步数据也变成了空的;如果master和slave都没有持久化,同样会面临数据的完全丢失。需要注意的是,即便是使用了哨兵(关于哨兵后面会有文章介绍)进行自动的主从切换,也有可能在哨兵轮询到master之前,便被自动拉起机制重启了。因此,应尽量避免“自动拉起机制”和“不做持久化”同时出现。

(4)异地灾备:上述讨论的几种持久化策略,针对的都是一般的系统故障,如进程异常退出、宕机、断电等,这些故障不会损坏硬盘。但是对于一些可能导致硬盘损坏的灾难情况,如火灾地震,就需要进行异地灾备。例如对于单机的情形,可以定时将RDB文件或重写后的AOF文件,通过scp拷贝到远程机器,如阿里云、AWS等;对于主从的情形,可以定时在master上执行bgsave,然后将RDB文件拷贝到远程机器,或者在slave上执行bgrewriteaof重写AOF文件后,将AOF文件拷贝到远程机器上。一般来说,由于RDB文件文件小、恢复快,因此灾难恢复常用RDB文件;异地备份的频率根据数据安全性的需要及其他条件来确定,但最好不要低于一天一次。

fork阻塞:CPU的阻塞

在Redis的实践中,众多因素限制了Redis单机的内存不能过大,例如:
当面对请求的暴增,需要从库扩容时,Redis内存过大会导致扩容时间太长;
当主机宕机时,切换主机后需要挂载从库,Redis内存过大导致挂载速度过慢;

以及持久化过程中的fork操作,下面详细说明。
首先说明一下fork操作:

父进程通过fork操作可以创建子进程;子进程创建后,父子进程共享代码段,不共享进程的数据空间,但是子进程会获得父进程的数据空间的副本。
在操作系统fork的实际实现中,基本都采用了写时复制技术,即在父/子进程试图修改数据空间之前,父子进程实际上共享数据空间;但是当父/子进程的任何一个试图修改数据空间时,操作系统会为修改的那一部分(内存的一页)制作一个副本。

虽然fork时,子进程不会复制父进程的数据空间,但是会复制内存页表(页表相当于内存的索引、目录);父进程的数据空间越大,内存页表越大,fork时复制耗时也会越多。

在Redis中,无论是RDB持久化的bgsave,还是AOF重写的bgrewriteaof,都需要fork出子进程来进行操作。如果Redis内存过大,会导致fork操作时复制内存页表耗时过多;而Redis主进程在进行fork时,是完全阻塞的,也就意味着无法响应客户端的请求,会造成请求延迟过大。

为了减轻fork操作带来的阻塞问题,除了控制Redis单机内存的大小以外,还可以适度放宽AOF重写的触发条件、选用物理机或高效支持fork操作的虚拟化技术等

AOF追加阻塞:硬盘的阻塞

在AOF中,如果AOF缓冲区的文件同步策略为everysec,则:在主线程中,命令写入aof_buf后调用系统write操作,write完成后主线程返回;fsync同步文件操作由专门的文件同步线程每秒调用一次。
这种做法的问题在于,如果硬盘负载过高,那么fsync操作可能会超过1s;如果Redis主线程持续高速向aof_buf写入命令,硬盘的负载可能会越来越大,IO资源消耗更快;如果此时Redis进程异常退出,丢失的数据也会越来越多,可能远超过1s。
为此,Redis的处理策略是这样的:主线程每次进行AOF会对比上次fsync成功的时间;如果距上次不到2s,主线程直接返回;如果超过2s,则主线程阻塞直到fsync同步完成。因此,如果系统硬盘负载过大导致fsync速度太慢,会导致Redis主线程的阻塞;此外,使用everysec配置,AOF最多可能丢失2s的数据,而不是1s。
AOF追加阻塞问题定位的方法:
(1)监控info Persistence中的aof_delayed_fsync:当AOF追加阻塞发生时(即主线程等待fsync而阻塞),该指标累加。
(2)AOF阻塞时的Redis日志:
Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.
(3)如果AOF追加阻塞频繁发生,说明系统的硬盘负载太大;可以考虑更换IO速度更快的硬盘,或者通过IO监控分析工具对系统的IO负载进行分析,如iostat(系统级io)、iotop(io版的top)、pidstat等。

总结


  • 持久化在Redis高可用中的作用:数据备份,与主从复制相比强调的是由内存到硬盘的备份。
  • RDB持久化:将数据快照备份到硬盘;介绍了其触发条件(包括手动出发和自动触发)、执行流程、RDB文件等,特别需要注意的是文件保存操作由fork出的子进程来进行。
  • AOF持久化:将执行的写命令备份到硬盘(类似于MySQL的binlog),介绍了其开启方法、执行流程等,特别需要注意的是文件同步策略的选择(everysec)、文件重写的流程。
  • 一些现实的问题:包括如何选择持久化策略,以及需要注意的fork阻塞、AOF追加阻塞等。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值