Redis的两种持久化策略原理与内存回收策略

磁盘与内存的区别

内存是计算机中硬盘数据喝CPU数据交换的中转站,属于临时存储器,随着操作随时改写的存储内容,断电后,内存中的信息会全部消失,内存容量较小,读取数据较快。

硬盘的存储介质是磁存储,考磁头读写,硬盘可以长期存储数据,不受断电影响。存储容量大。

redis持久化,就是将数据放到断电后数据不会丢失的设备中,也就是硬盘上,当恢复后,再将磁盘里的数据读取,写入内存中,式数据不会丢失。

Redis支持两种方式的持久化,一种是RDB方式、另一种是AOF(append-only-file)方式。前者会根据指定的规
则“定时”将内存中的数据存储在硬盘上,而后者在每次执行命令后将命令本身记录下来。两种持久化方式可以单独
使用其中一种,也可以将这两种方式结合使用

RDB方式

RDB是默认的持久化方案,在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件恢复数据。

父进程继续处理client请求,fork出来的子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照。
当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出.

--fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和
原进程一致,但是是一个全新的进程,并作为原进程的子进程。


每次快照持久化都是将内存数据完整写入到磁盘一次,并不 是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。

Redis会在以下几种情况下对数据进行快照
\1. 根据配置规则进行自动快照
\2. 用户执行SAVE或者GBSAVE命令
\3. 执行FLUSHALL命令
\4. 执行复制(replication)时

根据配置规则进行自动快照

Redis允许用户自定义快照条件,当符合快照条件时,Redis会自动执行快照操作。快照的条件可以由用户在配置文
件中配置。配置格式如下

save
第一个参数是时间窗口,第二个是键的个数,也就是说,在第一个时间参数配置范围内被更改的键的个数大于后面
的changes时,即符合快照条件。redis默认配置了三个规则
save 900 1
save 300 10
save 60 10000
每条快照规则占一行,每条规则之间是“或”的关系。 在900秒(15分)内有一个以上的键被更改则进行快照。
用户执行SAVE或BGSAVE命令
除了让Redis自动进行快照以外,当我们对服务进行重启或者服务器迁移我们需要人工去干预备份。redis提供了两
条命令来完成这个任务

\1. save命令

当执行save命令时,Redis同步做快照操作,在快照执行过程中会阻塞所有来自客户端的请求。当redis内存中的数
据较多时,通过该命令将导致Redis较长时间的不响应。所以不建议在生产环境上使用这个命令,而是推荐使用
bgsave命令

\2. bgsave命令

bgsave命令可以在后台异步地进行快照操作,快照的同时服务器还可以继续响应来自客户端的请求。执行BGSAVE
后,Redis会立即返回ok表示开始执行快照操作。
通过LASTSAVE命令可以获取最近一次成功执行快照的时间; (自动快照采用的是异步快照操作)

执行FLUSHALL命令

该命令在前面讲过,会清除redis在内存中的所有数据。执行该命令后,只要redis中配置的快照规则不为空,也就
是save 的规则存在。redis就会执行一次快照操作。不管规则是什么样的都会执行。如果没有定义快照规则,就不
会执行快照操作

执行复制时

该操作主要是在主从模式下,redis会在复制初始化时进行自动快照。
这里只需要了解当执行复制操作时,及时没有定义自动快照规则,并且没有手动执行过快照操作,它仍然会生成
RDB快照文件

优势

  1. 方便备份。
   2. 对比AOF,RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
   3.  RDB 可以最大化 Redis 的性能。

劣势

   1. 最后一次保存的数据可能会丢失。

    2. 在数据集比较庞大的时候fork会非常耗时。

AOF方式

以日志的形式来记录每个‘写操作’将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

当使用Redis存储非临时数据时,一般需要打开AOF持久化来降低进程终止导致的数据丢失。AOF可以将Redis执行
的每一条写命令追加到硬盘文件中,这一过程会降低Redis的性能,但大部分情况下这个影响是能够接受的,另外
使用较快的硬盘可以提高AOF的性能。

开启AOF

默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数启用,在redis.conf
中找到

 appendonly yes

开启AOF持久化后每执行一条会更改Redis中的数据的命令后,Redis就会将该命令写入硬盘中的AOF文件。AOF文
件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是apendonly.aof. 可以在redis.conf
中的属性 appendfilename appendonlyh.aof修改

AOF的实现

AOF文件以纯文本的形式记录Redis执行的写命令例如开启AOF持久化的情况下执行如下4条命令
set foo 1
set foo 2
set foo 3
get
redis 会将前3条命令写入AOF文件中,通过vim的方式可以看到aof文件中的内容

我们会发现AOF文件的内容正是Redis发送的原始通信协议的内容,从内容中我们发现Redis只记录了3条命令。然后这时有一个问题是前面2条命令其实是冗余的,因为这两条的执行结果都会被第三条命令覆盖。随着执行的命令越来越多,AOF文件的大小也会越来越大,其实内存中实际的数据可能没有多少,那这样就会造成磁盘空间以及redis数据还原的过程比较长的问题。因此我们希望Redis可以自动优化AOF文件,就上面这个例子来说,前面两条是可以被删除的。 而实际上Redis也考虑到了,可以配置一个条件,每当达到一定条件时Redis就会自动重写AOF文件,这个条件的配置

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

appendfsync (同步配置)

在启用AOF持久化策略时,涉及到一个问题,如何写入aof策略,有以下三种实现:

  1. everysec 每一秒执行一次。(默认)
  2. always 每次有更新redis,就写入aof
  3. no 表示从不主动写入aof文件中。

auto-aof-rewrite-percentage 表示的是当目前的AOF文件大小超过上一次重写时的AOF文件大小的百分之多少时会
再次进行重写,如果之前没有重写,则以启动时AOF文件大小为依据auto-aof-rewrite-min-size 表示限制了允许重写的最小AOF文件大小,通常在AOF文件很小的情况下即使其中有很多冗余的命令我们也并不太关心。
另外,还可以通过BGREWRITEAOF 命令手动执行AOF,执行完以后冗余的命令已经被删除了.
在启动时,Redis会逐个执行AOF文件中的命令来将硬盘中的数据载入到内存中,载入的速度相对于RDB会慢一些.

AOF的重写原理

Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前
数据集所需的最小命令集合。

重写的流程是这样,主进程会fork一个子进程出来进行AOF重写,这个重写过程并不是基于原有的aof文件来做
的,而是有点类似于快照的方式,全量遍历内存中的数据,然后逐个序列到aof文件中。在fork子进程这个过程
中,服务端仍然可以对外提供服务,那这个时候重写的aof文件的数据和redis内存数据不一致了怎么办?不用担
心,这个过程中,主进程的数据更新操作,会缓存到aof_rewrite_buf中,也就是单独开辟一块缓存来存储重写期间
收到的命令,当子进程重写完以后再把缓存中的数据追加到新的aof文件。

当所有的数据全部追加到新的aof文件中后,把新的aof文件重命名为,此后所有的操作都会被写入新的aof文件。如果在rewrite过程中出现故障,不会影响原来aof文件的正常工作,只有当rewrite完成后才会切换文件。因此这个
rewrite过程是比较可靠的.

比较

AOF优势
1.性能差但数据完整性好。
2.出现宕机现象也不会影响数据一致性。
3.保证数据安全性

劣势

1.相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb.

  1. aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同.

Redis内存回收策略

Redis中提供了多种内存回收策略,当内存容量不足时,为了保证程序的运行,这时就不得不淘汰内存中的一些对
象,释放这些对象占用的空间,那么选择淘汰哪些对象呢?

其中,默认的策略为noeviction策略,当内存使用达到阈值的时候,所有引起申请内存的命令会报错

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
适合的场景: 如果我们的应用对缓存的访问都是相对热点数据,那么可以选择这个策略

allkeys-random:随机移除某个key。
适合的场景:如果我们的应用对于缓存key的访问概率相等,则可以使用这个策略

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰。

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
适合场景:这种策略使得我们可以向Redis提示哪些key更适合被淘汰,我们可以自己控制

总结

实际上Redis实现的LRU并不是可靠的LRU,也就是名义上我们使用LRU算法淘汰内存数据,但是实际上被淘汰的键
并不一定是真正的最少使用的数据,这里涉及到一个权衡的问题,如果需要在所有的数据中搜索最符合条件的数
据,那么一定会增加系统的开销,Redis是单线程的,所以耗时的操作会谨慎一些。为了在一定成本内实现相对的
LRU,早期的Redis版本是基于采样的LRU,也就是放弃了从所有数据中搜索解改为采样空间搜索最优解。Redis3.0
版本之后,Redis作者对于基于采样的LRU进行了一些优化,目的是在一定的成本内让结果更靠近真实的LRU。

你可能感兴趣的:(Redis的两种持久化策略原理与内存回收策略)