Redis必学(一)redis RDB和AOF持久化

1 概述

  Redis持久化的目的是为了保存数据,在redis重启时可以恢复。但是在开发环境中,如果测试数据量大,每次都加载数据,很麻烦,如果遇到并不需要旧数据的场景,就更无需持久化了,所以我这里就先介绍取消持久化的方式,再介绍两种持久化方式(RDB和AOF)。
  redis在数据恢复时,会检测持久化策略,因为AOF的更新频率通常要比RDB高,所以如果Redis开启了AOF持久化功能,那么Redis会优先使用AOF文件来还原数据库。如果AOF处于关闭状态时,服务器才会使用RDB文件来还原数据库。
在这里插入图片描述

2 取消持久化

取消持久化无非就是设置配置文件:
设置 save “”,还要把持久化的本地文件干掉

  1. 设置 save "
  2. 将配置中dir所指向的目录的持久化文件删除。

3 RDB方式

  RDB方式的持久化是通过"快照"完成的,当符合一定条件时,Redis都会将内存中所有数据生成一份副本保存在硬盘上,这个过程即为“快照”。Redis默认将快照文件存储在Redis当前进程的工作目录中的dump.rdb文件中,可以通过dir和bfilename指定路径和文件名。Redis会在以下几种情况下对数据进行快照:

  • 根据配置文件中设置的配置进行自动快照;
  • 用户执行SAVE或BGSAVE命令;
  • 执行FLUSHALL命令
  • 进行主从复制时

3.1 根据配置文件设置进行自动快照

Redis允许用户自定义快照条件,设置格式为save M N。每当时间M内被修改的键个数大于N时,即复合自动快照条件。如:

save 90 1
save 300 10

同时可以存在多个条件,条件之间是或的关系,如上所示:表示在90s内有一个件被修改则进行快照,或者300ss内有10个键被修改则进行快照。

3.2 用户执行SAVE命令或者BGSAVE命令

  Redis除了可以自动进行快照外,在一些手动迁移数据的场合我们也需要手动进行快照。Redis提供了两个命令来完成这一任务。

3.2.1 save命令

  save命令,当执行save命令时,Redis同步进行快照,在执行快照过程中,会阻塞客户端请求。当数据量大时,会导致Redis长时间不相应,生产环境中应避免使用这种方式。

3.2.2 bgsave命令

  bgsave命令,手动执行快照时推荐使用这种方式。BGSAVE命令可以在后台异步地进行快照操作,在快照的同时可以继续响应客户端请求。
  执行bgsave后Redis会立即返回OK,开始执行快照操作,但是并不会自动返回执行结果,如果要知道快照是否完成,可以通过lastsave命令获取最近一次成功执行快照的时间戳。自动执行快照时和bgsave采用的策略相同,都是异步执行
在这里插入图片描述  在bgsave命令执行期间,服务器处理save,bgsave,bgrewriteaof(异步执行aof重写操作)三个命令的方式有所不同。

bgsave命令执行期间,服务器拒绝save命令;
bgsave命令执行期间,服务器拒绝bgsave命令;
bgsave命令执行期间,bgrewriteaof命令会被延迟到bgsave执行完毕之后再执行;

拒绝save和bgsave命令是防止产生竞争条件;

bgrewriteaof命令会被延迟到bgsave执行完毕之后再执行是因为bgsave和bgrewriteaof都是由子进程执行,这两个命令没什么冲突的地方,但是不同时执行是为性能考虑,因为这两个子进程都会执行大量磁盘写入操作。

3.2.3 执行FLUSHALL命令

  当执行FLUSHALL命令时,Redis会清除所有数据,但是如果我们在配置文件中设置了自动快照的条件,那么当执行FLUSHALL时,就会触发自动快照,无论是否达到设置中的条件。

3.2.4 执行复制

  当进行主从复制时,主节点将无条件执行快照。

3.3 RDB快照原理

  快照执行的基本思想如下:

  • Redis使用fork函数复制当前进程(父进程)的副本(子进程)
  • 父进程继续响应客户端请求,子进程开始讲内存中数据写入快照文件中。
  • 当子进程写入完所有数据后,会用该临时文件替换旧的RDB文件,完成快照。

  在执行fork函数时,是异步操作,也就是说fork函数发生的那一刻父子进程会共享同一内存数据,当父进程修改其中谋片数据库时(如执行一个写命令),操作系统会将该片数据复制一份以保证子进程的快照操作不受影响,这就意味着, 在执行快照命令之后的数据更新,并不会同步更新到快照文件中,RDB文件存储的就是执行fork函数那一刻的内存数据。
  同时,从以上描述可以得知,在执行快照期间,发生写数据,复制的只是更新数据的那一片区的数据,并不是全部数据,这就意味着执行fork内存后,内存的使用量并不会翻倍。但是同时我们需要将系统的设置为,允许应用程序申请超过可用内存。在/etc/sysctl.conf文件中加入vm.overcommit_memory = 1,然后重启系统确保生效。

  RDB方式持久化的优点:

  • 文件时刻都是完整的,不会存在文件不完整的情况,这个特点非常有利于数据保存。
  • 文件经过压缩,非常适合用来存储和传输数据。
  • 从硬盘加载文件到内存时,效率也很高。

  RDB方式持久化的缺点:

一旦Redis异常退回,就会丢失最后一次快照以后更新的数据。

3.4 RDB文件结构

一个完整的RDB文件包含以下几个部分:
在这里插入图片描述

  1. REDIS文件最开头的是REDIS字符串部分,这个部分的长度是5字节
  2. db_version长度为4字节,表示RDB文件的版本号,如0006
  3. database 包含0-n个数据库,以及各个数据库中的键值对数据。如果数据库状态为空,则这部分数据也为空;如果数据库状态为非空,那这部分也费控,并保存着数据库中的键值对。
  4. EOF常量为1字节,表示RDB文件正文内容结束,所有数据库的键值对已经载入完毕。
  5. check_sum 是一个8字节长的无符号整数,保存着一个校验和,这个和是由前面四部分计算得出,通过该数据校验,判断RDB文件是否出错或者损坏。

RDB文件总体结构为:
在这里插入图片描述

3.4.1 database部分

一个RDB文件的database可以保存任意多个非空数据库,如:
在这里插入图片描述
每个非空database都保存着SELECTDB、db_number、key_value_pairs三个部分:
在这里插入图片描述
SELECTDB:常量长度为1字节,表示接下来要读入的是一个数据库号码。
db_number:存储着数据库号码。通过该值可以实现数据库的切换,使读入后的键值对加载到正确的数据库中。
key_value_pairs:保存了数据库中所有键值对数据。如果键值对包含过期时间,那么过期时间也会和键值对保存在一起。
在这里插入图片描述

3.4.2 key_value_pairs部分

  不带过期时间的键值对在RDB文件中由TYPE、key、value三部分组成。
带过期时间的键值对在RDB文件中由EXPIRETIME_MS、ms、TYPE、key、value。

type记录了value的类型
key总是一个字符串对象,它的编码方式和REDIS_RDB_TYPE_STRING类型的value一样。根据内容长度的不同,key的长度也会不同
value保存了值对象,根据type中的值,来决定如何读入和解释value
EXPIRETIME_MS常量长度为1字节,表示接下来要读取以毫秒为单位的过期时间。
ms是一个字节长度,以毫秒为单位的UNIX时间戳,这个时间戳就是键值对的过期时间。

TYPE的值有以下几中:
REIDS_RDB_TYPE_STRING 字符串对象
REIDS_RDB_TYPE_LIST 列表对象
REIDS_RDB_TYPE_SET 集合对象
REIDS_RDB_TYPE_ZSET 有序集合对象
REIDS_RDB_TYPE_HASH 哈希表对象
REIDS_RDB_TYPE_LIST_ZIPLIST 压缩列表对象
REIDS_RDB_TYPE_SET_INTSET INSET编码的集合
REIDS_RDB_TYPE_ZSET_ZIPLIST 压缩有序集合对象
REIDS_RDB_TYPE_HASH_ZIPLIST 压缩哈希表对象

4 AOF持久化

  如果对数据丢失率有较高的要求,会开启AOF持久化来降低数据丢失。AOF会将Redis执行的每一条写命令追加到文件中,但是这一过程显然会降低Redis性能。

  默认情况下没有开启AOF持久化,可以通过appendonly参数启用:

appendonly yes

开启AOF之后,每执行一条写命令,Redis就会将该命令写入AOF文件中,AOF文件的默认存储位置和RDB文件的位置相同,通过dir和appendfilename来指定路径和文件名,默认文件名为appendonly.aof

4.1 AOF实现过程

AOF以纯文件的形式记录了Redis执行的写命令,如:

SET foo 1
SET foo 2
SET foo 3
GET foo

Redis会将SET* 三条命令写入AOF文件中:
在这里插入图片描述
  从上述命令可以看出,其实前两条命令都是冗余的,因为会被第三条命令覆盖。随着执行的命令越来越多,AOF文件也会越来越大,其中也有很多冗余命令,因此Redis会在达到一定条件时自动重写AOF文件,该条件可以在配置文件中设置:

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

  auto-aof-rewrite-percentage 参数的意义为当前aof文件大小超过上一次重写时的百分之100会进行再次重写,如果初次重写,则以启动时的AOF文件大小为依据。
  auto-aof-rewrite-min-size 限制了允许重写的最小AOF文件大小,通常AOF文件很小的情况下也没有必要重写。
  除此以外我们可以通过BGREWRITEAOF命令手动执行AOF重写。

4.2同步硬盘数据

  虽然每次执行写操作,AOF都会将命令记录在AOF文件中,但是事实上,由于操作系统缓存机制,数据并没有马上写入硬盘,而是写在了硬盘缓存,默认情况下,会每隔30s执行一次同步操作,将硬盘缓存中的数据持久化,如果在此期间断电,就会丢失数据,一般开启AOF的应用都无法忍受长达30s的数据丢失,因此我们需要通过配置appendfsync参数来设置同步时间:

appendfsyn always
appendfsyn everysec
appendfsyn no

  默认情况会选择everysec,每一秒执行一次同步,always表示每次写入都同步,这样最安全但是最慢,no表示系统默认值,不做特殊设置,这是最快但是最不安全的模式。一般情况默认情况下就够用了。

4.3 AOF重写实现

  AOF重写是新的AOF文件替换旧的AOF文件的功能,实际上AOF文件重写并不会对现有的AOF文件做任何读取、分析和写入操作,这个功能是通过读取服务器当前的数据库状态来实现的。
  比如:
在这里插入图片描述
在这里插入图片描述
  在重写前,AOF文件中写入六条命令。  如果服务器想要用尽量稍等少的命令来记录list键的状态,那么最直接到方法不是对现有的AOF文件做处理,而是直接从数据库读取list的键值,直接用一条PRUSH list “C” “D” “E” “F” “G”命来来代替原文件的6条命令。 通过这种方式,可以非常高效的重写AOF文件。

4.3 AOF后台重写

  Redis如果启动了AOF持久化功能,则会将AOF重写程序放在子进程里执行。

在子进程进行AOF重写期间,服务器进程(父进程)可以继续处理客户端请求
子进程带有服务器进程的数据副本,使用子进程而不是线程,可以避免使用锁的情况,保证数据的安全性。

  不过这里有个问题,如果在重写过程中,主进程在持续接收客户端请求,新的命令会对数据库数据进行修改,这就使得重写后的AOF文件所保存的数据和数据库当前的状态不一致。
  为了解决这个问题,Redis设置了一个AOF重写缓冲区,这个缓冲区存储着在AOF文件重写期间,主进程所接收到的命令,同时还会将这个写命令发送到AOF缓冲区。如图所示:
在这里插入图片描述
这就意味着,在AOF重写期间,服务器进程需要执行以下三个工作:

  1. 接收客户端命令
  2. 将执行后的写命令追加到AOF缓冲区
  3. 将执行后的写命令追加到AOF重写缓冲区。

这样一来就可以保证:

- AOF缓冲区的内容会定时写入到AOF文件中,这是AOF文件的正常写入过程
- 从创建AOF重写子进程开始,服务器执行的所有写明了会被记录在AOF重写换冲区里。

当子进程AOF重写工作后,会将AOF重写缓冲区的所有内容写入到新的AOF文件中。

AOF的优点:

  1. 数据丢失少

AOF的缺陷:

  1. 数据恢复时,加载慢
  2. 开启AOF时会在一定程度上影响性能。

5 总结

  Redis允许同时开启AOF和RDB,既保证了数据的安全,又使得备份操作容易进行。重启Redis时可以通过AOF文件来恢复数据。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值