简介
常用的如MySQL等数据库数据是存储在硬盘中,我们都是通过硬盘去读写数据(MySQL也会有把数据存内存中加快读取的机制)。而Redis的数据是存储在内存中,这使得读写数据的速度大大加快,但是由于系统的特性,在Redis进程关闭的时候,Redis进程所占用的内存会释放,数据也跟着释放。
使用硬盘做存储媒介的数据库,因为硬盘存储的数据会一直存在(不考虑硬盘损坏),基本不需要考虑数据丢失的问题。但是使用Redis,一旦出现Redis进程崩溃、系统崩溃、手动重启Redis等涉及Redis进程的操作,Redis中的数据会丢失。所以学习Redis的数据持久化是必须的。
数据持久化就是将原本存在内存中容易丢失的数据,按照一定的方案把数据存到硬盘中,从而大大降低数据丢失的可能性。Redis提供的数据持久化方案有两种:RDB(Redis Database)和AOF(append-only file)。这两种方案可以同时使用,也可以单独使用。
注:持久化的本质是定时或定量把原本存在内存中的数据存到硬盘中,持久化方案的选择会影响到Redis的性能,所以需要根据实际情况选择适合自己架构的方案,减少由于持久化导致的性能下降
RDB
RDB也称作快照持久化,RDB就是可以将存在于某一时刻的所有数据都写入硬盘,相当于生成了一个快照。
触发生成快照的方式
- 客户端向Redis发送BGSAVE命令(windows平台不支持该方式)。服务器端的Redis收到BGSAVE后,会调用fork命令创建子进程(创建子进程过程中,父进程停止响应命令,后面会细讲),子进程负责将快照写入硬盘,父进程继续处理命令。
- 客户端向Redis发送SAVE命令。服务器端Redis收到SAVE后,开始生产快照文件,而且会停止响应任何其他命令直到快照创建完毕。(由于SAVE命令导致的阻塞时间较长,一般情况下不使用该命令)。
- 在配置文件中设置了save选项。比如 save 60 1000,表示Redis最近一次创建快照之后开始算起,当“60秒内有1000次写入”这个条件被满足时,Redis就会自动触发BGSAVE命令。可配置多个save选项,当任意一个save的条件被满足时,Redis则会触发BGSAVE命令。
- 当使用主从Redis时,从服务器(slave)向主服务器(master)发送SYNC命令请求进行一次复制操作时,如果主服务器目前没有在执行BGSAVE操作,或者主服务器并非刚刚执行完BGSAVE操作,那么主服务器就会执行BGSAVE命令。
一般情况下我们使用Redis自带的功能(配置save选项)来按需持久化数据,而不需要手动触发去生成快照。打开save配置的情况下,在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中,即在指定目录下生成一个dump.rdb文件。
RDB相关配置
配置项 | 参数 | 说明 |
---|---|---|
save | seconds changes |
在seconds秒内有changes次写操作,则触发生成快照 |
stop-write-on-bgsave-error | yes / no | bgsave发生错误时,是否禁止修改redis的数据 |
rdbcompression | yes / no | 生成快照时是否压缩数据(压缩数据:减少快照文件大小,但是因为多了压缩的操作,增加了生成快照的成本) |
dbfilename | filename | 生成的快照文件命名 |
风险
部分数据丢失
save配置项使得Redis可以根据预先设置的条件来按需备份数据。但是由于两次生成快照之间肯定存在时间差,在第一次快照生成成功,第二次快照还没生成的情况下,如果系统出现崩溃等情况,会导致只保留了第一次快照的数据,在第一次快照生成后的所有写操作都将无效(相当于数据丢失)。这部分的数据丢失对于不同系统影响程度各不相同,部分系统使用Redis来做数据缓存,即数据本来是存在数据库或者文件中的,使用Redis缓存数据只是为了加快数据读取,这种情况下数据丢失是可以接受的。
性能影响
上面讲到触发生成快照的第一个方法中,提到创建子进程过程中父进程停止响应命令,现在引入《Redis实践》书中的一部分来大概说明这个情况。
引自 4.1持久化选项 章节
当Redis存储的数据量只有几个GB的时候,使用快照来保存数据是没有问题的。Redis会创建子进程并将数据保存到硬盘里面,生成快照所需的时间比你读这句话所需的时间还要短。但随着Redis占用的内存越来越多,BGSAVE在创建子进程时耗费的时间也会越来越多。
……执行BGSAVE而导致的停顿时间有多长取决于Redis所在的系统:对于真实的硬件
VMWare虚拟机或者KVM虚拟机来说,Redis进程每占用一个GB的内存,创建该进程的子进程所需的时间就要增加10~20毫秒。
……为了防止Redis因为创建子进程而出现停顿,我们可以考虑关闭自动保存,转而通过手动发送BGSAVE或者SAVE来持久化。……
另一方面,虽然SAVE、会一直阻塞Redis直到快照生成完毕,但是因为它不需要创建子进程,所以就不会像BGSAVE一样因为创建子进程而导致Redis停顿;并且因为没有子进程在争抢资源,所以SAVE创建快照的速度会比BGSAVE创建快照的速度要来的更快一些。
AOF
AOF持久化就是将每一次的写命令都按顺序记录下来,在需要恢复数据的时候,把记录下来的命令重新执行一遍就可以了。
要开启AOF持久化,只需要配置 appendonly yes 就可以了。
配置项 | 参数 | 说明 |
---|---|---|
appendonly | yes / no | 是否开启AOF持久化 |
apendfsync | always everysec no |
同步频率 always 每个写命令都同步写入硬盘 everysec 每秒同步一次 no 让操作系统来决定何时进行同步 |
no-appendfsync-on-rewrite | yes / no | AOF重写时是否暂停数据同步(写入AOF文件) |
上面介绍RDB的时候讲到,RDB在进程意外关闭的情况下会丢失最后一次生成快照之后的所有写操作记录。当AOF的apendfsync配置为always时,能解决丢失写操作记录的问题,但是因为每次写操作都同步写操作到硬盘,这很大程度上影响了Redis的效率。如果既想保持Redis性能,又想尽可能不丢失数据,apendfsync配置为everysec是比较合适的。
AOF重写(Rewrite)
开启AOF后,每个写操作都会记录到硬盘,假如有如下的一组操作:
redis> incr read_num
(integer) 1
redis> incr read_num
(integer) 2
redis> set read_num
OK
redis> incr read_num
(integer) 1
上面命令对 read_num 这个key进行了4次操作,AOF会把4条命令都写到硬盘中,这里会有几个要注意的地方:
- 对于持久化来说,我们只需要在恢复备份的时候,恢复 read_num 最后的值,而不关心它是经过什么步骤最终得到这个值
- 每个写操作都记录到AOF文件中,AOF文件会不断扩大,有撑爆磁盘的可能
- Redis恢复备份时,要把AOF文件的写操作重新执行一遍,如果写命令过多会导致Redis恢复备份需要消耗很长的时间。
为了帮AOF文件“减肥”,引入了AOF重写的功能。AOF重写不会遍历旧AOF文件,而是根据现有内存中的数据生成命令。
触发AOF重写
- 客户端可以发送BGREWRITEAOF命令
- 使用 auto-aof-rewrite-percentage 配置
- 使用 auto-aof-rewrite-min-size 配置
AOF重写工作原理
- 收到BGREWRITEAOF命令时,Redis会创建一个子进程,由子进程根据当前内存数据生成一系列创建命令(例如上面例子中的read_num,会直接生成 set read_num 1 命令)并保存到新AOF文件中(临时文件)。
- 在创建子进程的同时,还会生成一个AOF重写缓存。在重写AOF文件过程中,如果Redis收到新的写操作,会对写操作进行如下处理:
- 更新对应数据在内存中的值
- 将写操作记录到旧的AOF文件中(避免重写失败导致写操作丢失)
- 将写操作记录到AOF重写缓存中
- 子进程重写完成后,会发送一个完成信号给父进程,父进程接收到信号后会将AOF重写缓存中的命令全部写入新AOF文件中,然后用新AOF文件覆盖旧AOF文件。
风险
- AOF文件不断增大,撑爆硬盘。
- 同步频率的选择影响Redis性能,过于频繁(always)会导致性能下降严重。
- AOF不进行重写的话,重启Redis时因要执行的命令过多导致重启速度变慢。
总结
RDB占用磁盘空间小,恢复备份速度快,但是数据完整性欠佳。
AOF数据完整程度高,但是占用磁盘空间大,恢复备份速度慢,相对来说性能差一点。
资料参考
《Redis实战》4.1章节
网友评论