前言
通常 Redis 将数据存储在内存中或虚拟内存中,它是通过以下两种方式实现对数据的持久化。
RDB 快照方式(默认持久化方式)
这种方式就是将内存中数据以快照的方式写入到二进制文件中 ,默认的文件名为dump.rdb。
手动触发
客户端可以使用 save 或者 bgsave 命令通知 redis 做一次快照持久化。
save 操作是在主线程中保存快照的,由于 redis 是用一个主线程来处理所有客户端的请求,这种方式会阻塞所有客户端请求。所以不推荐使用。
bgsave 操作时Redis主进程会fork一个子进程来完成RDB的过程,完成后自动结束(操作系统的多进程Copy On Write机制,简称COW)。所以Redis主进程阻塞时间只有fork阶段的那一下。相对于save,阻塞时间很短。
另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步增量数据。如果数据量大的话,写操作会比较多,必然会引起大量的磁盘 IO 操作,可能会严重影响性能。
注意:由于快照方式是在一定间隔时间做一次的,所以如果 redis 意外当机的话,就会丢失最后一次快照后的所有数据修改。
自动触发
场景一:配置redis.conf,触发规则,自动执行:
# 当在规定的时间内,Redis发生了写操作的个数满足条件,会触发发生BGSAVE命令。 # save <seconds> <changes> # 当用户设置了多个save的选项配置,只要其中任一条满足,Redis都会触发一次BGSAVE操作 save 900 1 save 300 10 save 60 10000 # 以上配置的含义:900秒之内至少一次写操作、300秒之内至少发生10次写操作、 # 60秒之内发生至少10000次写操作,只要满足任一条件,均会触发bgsave
场景二:执行shutdown命令关闭服务器时,如果没有开启AOF持久化功能,那么会自动执行一次bgsave
场景三:主从同步(slave和master建立同步机制)
RDB 执行过程
Redis 使用操作系统的多进程 cow(Copy On Write) 机制来实现RDB快照持久化
执行bgsave命令的时候,Redis主进程会检查是否有子进程在执行RDB/AOF持久化任务,如果有的话,直接返回
Redis主进程会fork一个子进程来执行执行RDB操作,fork操作会对主进程造成阻塞(影响Redis的读写),fork操作完成后会发消息给主进程,从而不再阻塞主进程。(阻塞仅指主进程fork子进程的过程,后续子进程执行操作时不会阻塞)
RDB子进程会根据Redis主进程的内存生成临时的快照文件,持久化完成后会使用临时快照文件替换掉原来的RDB文件。(该过程中主进程的读写不受影响,但Redis的写操作不会同步到主进程的主内存中,而是会写到一个临时的内存区域作为一个副本)
子进程完成RDB持久化后会发消息给主进程,通知RDB持久化完成(将上阶段内存副本中的增量写数据同步到主内存)
RDB的优缺点
优点
RDB文件小,非常适合定时备份,用于灾难恢复
Redis加载RDB文件的速度比AOF快很多,因为RDB文件中直接存储的是内存数据,而AOF文件中存储的是一条条命令,需要重演命令。
缺点:
RDB无法做到实时持久化,若在两次bgsave间宕机,则会丢失区间(分钟级)的增量数据,不适用于实时性要求较高的场景
RDB的cow机制中,fork子进程属于重量级操作,并且会阻塞redis主进程
存在老版本的Redis不兼容新版本RDB格式文件的问题
AOF 增量持久化
AOF日志是持续增量的备份,是基于写命令存储的可读的文本文件。
AOF日志会在持续运行中持续增大,由于Redis重启过程需要优先加载AOF日志进行指令重放以恢复数据,恢复时间会无比漫长。
所以需要定期进行AOF重写,对AOF日志进行瘦身。目前AOF是Redis持久化的主流方式。
开启方式
AOF默认是关闭的,通过redis.conf配置文件进行开启
## 只有在“yes”下,aof重写/文件同步等特性才会生效 appendonly yes ## 指定aof文件名称 appendfilename appendonly.aof ## 指定aof操作中文件同步策略,有三个合法值:always everysec no,默认为everysec appendfsync everysec ## 在aof-rewrite期间,appendfsync是否暂缓文件同步,"no"表示“不暂缓”,“yes”表示“暂缓”,默认为“no” no-appendfsync-on-rewrite no ## aof文件rewrite触发的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才会触发rewrite,默认“64mb”,建议“512mb” auto-aof-rewrite-min-size 64mb ## 相对于“上一次”rewrite,本次rewrite触发时aof文件应该增长的百分比 ## 每一次rewrite之后,redis都会记录下此时“新aof”文件的大小(例如A) ## aof文件增长到A*(1 + p)之后,触发下一次rewrite,每一次aof记录的添加,都会检测当前aof文件的尺寸。 auto-aof-rewrite-percentage 100
AOF是文件操作,对于变更操作比较密集的server,那么将造成磁盘IO的负荷加重。
此外linux对文件操作采取了“延迟写入”手段,即并非每次write操作都会触发实际磁盘操作,而是进入了buffer中,当buffer数据达到阀值时触发实际写入(也有其他时机),这是linux对文件系统的优化。这样的持久化还是有可能会丢失部分修改。
所以Linux 的glibc提供了fsync(int fd)函数可以将指定文件的内容强制从内核缓存刷到磁盘。只要 Redis 进程实时调用 fsync 函数就可以保证 aof 日志不丢失。
但是 fsync 是一个磁盘 IO 操作,它很慢!如果 Redis 执行一条指令就要 fsync 一次,那么 Redis 高性能的地位就不保了。
因此在上述配置文件中,可观察到Redis中提供了3中AOF记录同步选项:
- always:每一条AOF记录都立即同步到文件,性能很低,但较为安全。
- everysec:每秒同步一次,性能和安全都比较中庸的方式,也是redis推荐的方式。如果遇到物理服务器故障,可能导致最多1秒的AOF记录丢失。
- no:Redis永不直接调用文件同步,而是让操作系统来决定何时同步磁盘。性能较好,但很不安全。
重写机制
日志追加方式同时带来了另一个问题。持久化文件会变的越来越大。此时就需要对AOF进行重写,瘦身。
AOF Rewrite 虽然是“压缩”AOF文件的过程,但并非采用“基于原AOF文件”来重写或压缩,而是采取了类似RDB快照的方式:基于Copy On Write,全量遍历内存中数据,然后逐个序列到AOF文件中。因此AOF rewrite能够正确反应当前内存数据的状态。
AOF重写(bgrewriteaof)和RDB快照写入(bgsave)过程类似,二者都消耗磁盘IO。Redis采取了“schedule”策略:无论是“人工干预”还是系统触发,快照和重写需要逐个被执行。
重写过程中,对于新的变更操作将仍然被写入到原AOF文件中,同时这些新的变更操作也会被Redis收集起来。当内存中的数据被全部写入到新的AOF文件之后,收集的新的变更操作也将被一并追加到新的AOF文件中。然后将新AOF文件重命名为appendonly.aof,使用新AOF文件替换老文件,此后所有的操作都将被写入新的AOF文件。
AOF重写默认大小64m太小 可改5g以上 避免重写频繁,在4.0之前会将相同的操作进行合并(比如连续使用set进行了三次,重写会将这三次合并成一次命令存到文件中,这样文件就会变小),在 4.0之后是混合持久化。
重写触发机制
和RDB类似,AOF触发机制也分为:手动触发和自动触发
手动触发
直接调用bgrewriteaof命令
redis-cli -h ip -p port bgrewriteaof
自动触发
根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机
auto-aof-rewrite-min-size:表示运行AOF重写时文件最小体积,默认为64MB(我们线上是512MB)。
auto-aof-rewrite-percentage:代表当前AOF文件空间(aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的值其中aof_current_size和aof_base_size可以在info Persistence统计信息中查看。
AOF 优缺点
优点
AOF只是追加写日志文件,对服务器性能影响较小,速度比RDB要快,消耗的内存较少
缺点
AOF方式生成的日志文件太大,需要不断AOF重写,进行瘦身。
即使经过AOF重写瘦身,由于文件是文本文件,文件体积较大(相比于RDB的二进制文件)。AOF重演命令式的恢复数据,速度显然比RDB要慢。
Redis 4.0 混合持久化
- 仅使用RDB快照方式恢复数据,由于快照时间粒度较大,会丢失大量数据。
- 仅使用AOF重放方式恢复数据,日志性能相对 rdb 来说要慢。在 Redis 实例很大的情况下,启动需要花费很长的时间。
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。
混合持久化同样也是通过bgrewriteaof完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据。
大量数据使用粗粒度(时间上)的rdb快照方式,性能高,恢复时间快。增量数据使用细粒度(时间上)的AOF日志方式,尽量保证数据的不丢失。
开启混合持久化
4.0版本的混合持久化默认关闭的,通过aof-use-rdb-preamble配置参数控制,yes则表示开启,no表示禁用,默认是禁用的,可通过config set修改。
在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。
持久化设置
- RBD和AOF建议同时打开(Redis4.0之后支持)
- RDB做冷备,AOF做数据恢复(数据更可靠)
- RDB采取默认配置即可,AOF推荐采取everysec每秒策
数据备份
我们需要定时备份rdb文件来做冷备,为什么?
不是有aof和rbd了吗为什么还要单独写定时任务去备份?
因为Redis的aof和rdb是仅仅有一个最新的,比如谁手贱再Redis宕机的时候执行rm -rf aof/rdb了,那不就GG了吗?或者rdb/aof文件损坏了等不可预期的情况。所以我们需要单独备份rdb文件以防万一。
数据恢复
redis挂了
如果仅仅是redis进程挂了,那么直接重启redis进程即可,Redis会按照持久化配置直接基于持久化文件进行恢复数据。
如果有AOF则按照AOF,AOF和RDB一起开的话也走AOF。
持久化文件丢了
如果持久化文件(rdb/aof)损坏了,或者直接丢失了。那么就要采取我们上面所做的rdb备份来进行恢复了。
方案一:直接把备份的rdb扔到redis持久化目录下然后重启redis
不行的原因在于:redis是按照先aof后rdb进行恢复的,所以都是开启aof的,redis启动后会重新生成新的aof文件,里面是空的。所以不会进行任何数据恢复,也就是说虽然你把rdb丢给redis了,但是redis会按照aof来恢复,而aof是redis启动的时候新生成的空文件,所以不会有任何数据进行恢复。
方案二:那么我们把rdb文件丢给redis后,先将redis的aof关闭再启动redis进程不就能按照rdb来进行恢复了吗?
是这样的,没毛病!但是新的问题来了,我们aof肯定要开的,aof对数据保障更可靠。那什么我们按照rdb文件恢复完后再修改redis配置文件开启aof然后重启redis进程不就得了嘛?大哥…你打开aof然后重启redis,这时候redis又会生成一个空的aof文件,这时候恢复的时候又是啥数据都没了。
可行方案:我不管你是持久化文件丢了还是坏了,我都先rm -rf * 给他删了。
- 停止redis进程
- 删除坏掉的rdb和aof持久化文件。
- 修改配置文件关闭redis的aof持久化。
- 找到最新备份的rdb文件扔到redis的持久化目录里。(这里最新的肯定是按照小时备份的最后一个)
- 启动Redis进程
- 执行set appendonly yes动态打开aof持久化。
- 等aof文件生成后再修改redis配置文件打开aof。
- 重启redis进程。
数据迁移
scan+MIGRATE
先scan出key,然后使用redis自带命令MIGRATE执行建的迁移。
说明:MIGRATE命令在redis>=2.6.0版本才有,Redis3.06版本之后支持迁移多个键。dump
现在源redis执行dump命令,该redis的所有数据会序列化成rdb文件;然后把rdb文件拷贝到目标redis,重启目标redis。
说明:这种方式优点是操作简单,不需要研发再另外写程序迁移。缺点是:需要运维手动操作。scan+hset/set
先scan出key,然后判断这个key属于哪种类型,执行对应的写命令hset、set等。