Redis持久化-RDB与AOF

一、概述

    Redis是一个高性能的内存数据库,也是NoSQL数据库的一种实现,与memcache一样能够提供高性能的数据存取操作,常用作分布式缓存,解决数据库在高并发访问时的性能问题。Redis与memcache相比,其中一个很大的特性是Redis提供数据持久化,即可以将内存中的数据保存到磁盘上,从而保证数据在出现如断电或进程崩溃时,不丢失。所以在实际运用中,在既需要保证数据安全性,又要保证在高并发访问时的性能,常常使用Redis作为项目的数据库来使用,而Redis提供了两种主要的数据持久化方案,分别为RDB和AOF。

二、RDB:快照持久化

    RDB是Redis的默认持久化方案,是将Redis数据库某个时间点的全部数据保存到一个叫做dump.rdb的二进制文件当中。具体何时触发RDB有两种方式:

    一种为在redis-cli客户端终端或者程序API客户端直接使用命令BGSAVE或SAVE,其中BGSAVE为通过fork一个子进程的方式,在子进程中负责读取Redis数据并保存到dump.rb文件,此时主进程可以继续处理客户端请求。fork子进程使用了操作系统的COW,即写时拷贝技术,具体后面介绍;而SAVE则是在Redis主进程中进行该过程,而我们知道Redis的单进程单线程处理客户端请求的,所以要尽量避免使用该命令,否则会阻塞所有的客户端读写操作,如果数据量很大,则可能造成服务器长达几分钟甚至几小时的停顿。

    另外一种为在redis.conf配置文件中,配置在一定时间段内,有多少key被修改了则自动采用BGSAVE进行RDB持久化,如下为配置的格式:

370f413b2a22934cef984b88385e972c7c0.jpg

由以上配置说明可知,分别为900秒内有一个key,300秒内有10个key,60秒内有10000个key发送修改则执行一次RDB,注意是after,所以检测周期,如果是save 60 10000,则是每60秒检测一次,如果50秒时,有超过10000个key被修改了,也是在60秒的时候执行RDB。

如果需要关闭RDB,则注释掉以上save 900 1这些,打开save ""注释,重启Redis;或者在redis-cli使用CONFIG SET SAVE "",关闭,不过使用redis-cli记得在redis.conf加上,否则下次启动会继续开启RDB。

    RDB执行的过程为:

    1. Redis主进程fork一个子进程,由子进程负责将当前Redis的数据快照写到dump.rdb,持久化到磁盘中。由于fork子进程使用的是操作系统的写时拷贝特性,故主进程和子进程是共享相同的物理地址空间的,即fork子进程被不需要将整个主进程整个内存物理地址空间复制一遍,在任何一个进程需要执行写操作时,Redis中是Redis主进程接收到客户端的写请求时,需要将相应的内存页复制出来,注意是被修改的内存页,而不是整体进程的物理内存。同时根据操作系统内存分段分页的实现,fork一个子进程虽然不需要拷贝整个物理地址空间,但是在子进程需要生成进程物理内存的页码表,所以如果Redis内存太大的话,根据每个页码8 bytes的实现,内存页4K,如果是40G的大小,则页码表的大小为80M,所以fork一个子进程也会挺耗时,进而导致Redis主进程卡顿,影响处理客户端的请求,造成客户端请求缓慢。可以通过命令:

info stats查看Redis的运行中的各项统计数据,其中latest_fork_usec(单位为微妙),即为最近一次bgsave主进程fork子进程的耗时,即主进程停顿了多久。如下:

a4a1afbf8420bdf7d09835b1385f99e3310.jpg

lastsave命令查看最近一次bgsave的耗时,对应info命令下的:rdb_last_save_time

新浪推荐单个Redis实例保存数据最大大小不超过20G,如果太大则考虑采用Redis分片,分片到多个Redis实例中。同时操作系统内存要预留30到45%的空闲内存,避免在执行BGSAVE期间,客户端大量写请求到来,导致Redis主进程复制大量内存页,使得内存耗尽。具体可参考:包含了详细的图文解析。

http://ningg.top/computer-basic-theory-copy-on-write/

    2. 子进程读取Redis数据被写到一个临时文件,主进程继续处理客户端的请求,如果是写请求,则复制对应的内存页;

    3. 子进程写数据完毕,则使用临时文件替换原来的RDB文件,子进程退出。

    RDB的优点:

    (1)RDB通过设置保存点,在达到保存点条件时,将当前Redis全部数据的快照保存到一个文件中,而在达到保存点之前主进程可以专注于处理客户端请求,无需fork子进程执行备份,进行频繁磁盘写操作,所以性能比AOF好。

    (2)快照包含当前时间点Redis的全部数据,所以非常适合进行数据备份。同时如果数据安全性不是很重要,能够容忍数据丢失,也可以不采用保存点机制,即在redis.conf文件设置save "",而是在空闲时间点,如凌晨通过脚本的方式,主动调用bgsave执行数据备份。

    (3)对于Redis服务重启速度而言,与AOF相比,RDB文件相对较小,所以通过RDB的方式加载数据,启动速度较快。同时也更方便数据传输,如传给slave节点。

    RDB的缺点:

    (1)RDB是在达到保存点或者主动调用SAVE或BGSAVE才执行,如果这个时间间隔内系统发生故障,则会造成这段时间内对数据的写丢失,所以数据存在丢失风险。

    (2)RDB是保存当前Redis的全部数据,如果数据量太大,则fork子进程可能会耗时较多阻塞主进程,如果在子进程执行期间,主进程接收客户端大量写操作,根据写时拷贝原理,可能造成服务器内存暴涨,所以需要预留较大的空闲内存避免内存溢出导致Redis进程被操作系统杀死。同时数据量太大时,也会造成子进程写磁盘操作频繁,如果服务器上面还有其他应用,如主应用自身,则会使得服务整体性能受到影响。

三、AOF:文件追加写命令持久化

    1. 简介

    AOF是以文件追加的方式,在每次对Redis执行写操作时,将写操作命令追加到AOF文件当中,所以对应的AOF文件可能会增长很快,与RDB相比,在Redis重启时,如果使用AOF文件reload数据的话,耗时会比较多。如果应用的数据需要高度的安全性,不容忍丢失,则采用AOF持久化可最大程度的保证数据的安全性,但是由于频率的执行AOF写操作,Redis整体读写性能会有所下降。在redis.conf配置如下:yes则为开启,默认为关闭:

6fa89ceb058c255ab8dcdfe096be2f12187.jpg

AOF文件的内容格式:Redis协议的文本,如set hello world

18494333a87710c6c7c1f9f4a9cbceb959a.jpg

    2. AOF重写

    定义:将Redis进程内的数据转化为写命令保存到新的AOF文件中。

    为了解决AOF增长过快导致文件太大,AOF也支持AOF文件重写,发生AOF重写时,过程与RDB类似,也是fork一个子进程来重写AOF文件,如将多条相同的命令压缩成一条等价的命令,去除无效命令,从而使AOF文件减少,AOF文件重写是安全的,因为在完成重写之前是不会对原来的AOF文件进行操作,而是成功完成后才替换。同时在AOF重写期间,对于主进程的写操作,会缓存在一个主进程的缓冲区中,同时主进程会继续追加旧的AOF文件。等子进程完成AOF重写,则通知主进程,主进程再将缓冲区数据追加到新的AOF文件中,最后替换AOF文件。

    触发方式:

(1)手动触发:bgrewriteaof

(3)自动触发:auot-aof-rewrite-min-size(默认64M)和auot-aof-rewrite-min-percentage(当前与上次重写后aof文件的比值)

   3. fsync

    定义:将AOF文件同步到磁盘。

    在Redis运行过程中,对于每个写命令都会追加到AOF文件当中,而根据操作系统的知识,不是每次追加都会发起一次系统调用写到磁盘中,而是会缓冲在一个页缓冲区中,操作系统会在缓冲区满或者规定时间间隔之后,写到磁盘中。所以如果将该缓冲区刷磁盘的操作交给操作系统去做,则可能会出现较大延迟,造成如果期间发生服务器宕机导致数据丢失。所以Redis提供了一种机制来控制将缓冲区数据刷到磁盘,在redis.conf文件配置如下:

0dbd7f63c584d5e0d72aae7f9b45b6af3ea.jpg

包括三个选项:always, everysec, no

always:为每次客户端发送写请求,在将写命令追加到AOF文件都执行刷磁盘,这个安全性最高但是性能最差,因为主进程在写磁盘时会阻塞,不能接受客户端的请求;

everysec:每秒执行一次,即将这秒内追加到AOF文件(文件缓冲区)的写命令写到磁盘去,这是通过一个专门的线程调用的,所以不会造成主线程阻塞,主进程可以继续处理客户端的请求;这个配置是系统默认的也是推荐的,最多只会造成1秒时间内的数据丢失,性能上跟RDB差不多;

no:Redis自身不主动执行写磁盘操作,而是交给操作系统去决定什么时候执行写磁盘。具体会发生在Redis关闭,AOF功能被关闭,操作系统的文件缓冲区满了或者定期保存操作被执行。

    AOF的优点:

    (1)每个写命令都实时追加到AOF文件当中,在服务器宕机时,数据可能造成的丢失较少,所以数据安全性较高;

    (2)AOF文件内容为一行行Redis协议的命令,可读性较好,同时如果不小心执行错了命令,则可以马上停机,将aof文件最后的命令删掉再重启Redis即可。

    AOF的缺点:根据使用fsync的策略,AOF性能可能慢于RDB,在大量写操作时,RDB提供了更好的最小延迟保证,而如果使用everysec策略,则性能差不多。同时AOF的文件会比RDB大,服务重启时较慢。

四、拓展思考

    1. RDB为什么使用fork子进程而不是子线程的方式?

    采用子进程而不采用子线程,主要是因为进程的地址空间是相互独立的,同一个进程的多个线程是共享该进程的地址空间的,所以如果Redis采用子线程的方式,则主线程和子线程就会存在竞争关系,需要进行加锁避免并发读写,这样就会影响主线程处理客户端的请求,使得性能下降。

    2. 如果不采用Redis自身提供的这两种持久化方案,是否可以自身实现持久化?

    如果不采用Redis自身提供的数据这两种持久化方案,一种方案是可以考虑将写命令发送到一个mq,如kafka,在通过mq发送到Redis,从而可以在mq中持久化写命令,类似于aof的实现,如下图。不过这种方案要将所有请求都通过mq,不然会影响Redis单线程排队处理所有请求的特性,造成数据读写顺序与程序希望的不一样,这个可能造成严重事故。

c7f1230babfaaa1bf843156b4cba90d057d.jpg

    另外一种方案可以参考云风大神提出的:https://blog.codingnow.com/2014/03/mmzb_redis.html,简单来说就是使用一个监控服务,监控堆Redis的读写,在执行写操作并且执行成功时,把这个数据操作的id或命令发送给监控服务,在监控服务将这个新写的数据或命令读回来,并保存到监控服务本地。

    同时关闭bgsave,Redis性能可能会得到提升,可参考如下讨论:https://www.v2ex.com/t/297741

转载于:https://my.oschina.net/u/3739573/blog/2245462

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值