为什么持久化?
持久化是Redis保障高可用性的基础。因为Redis是一个内存数据库,它的数据均保存在内存中,如果Redis实例发生宕机,在没有数据备份前提下,你的生产数据将会全部丢失。这时完善的持久化机制,可以把内存中的数据持久化到磁盘上,方便我们进行备份数据和快速恢复数据。
但是持久化就一定能保证数据不丢失吗?
首先我们要了解Redis为了保证高可用性,一般都是从一下几个方面入手的:
- 数据持久化
- 主从复制
- 自动故障恢复
- Redis集群
常见的Redis持久化有两种:(1)RDB快照持久化(2)AOF持久化,区别在于RDB持久化保存完整的数据,AOF缓存的是redis指令(当然我们该可以简化AOF缓存的指令)。缺点也很明显,RDB是某一时刻的数据快照,因此它的数据并不全,生成RDB文件的代价是比较大的,它会消耗大量的CPU和内存资源。RDB持久化适用于主从全量同步数据、数据库备份。AOF适合做增量数据的持久化。Redis启动会优先加载AOF文件进行数据恢复。
AOF 持久化
什么是 AOF 持久化
AOF(Append Only File):通过保存数据库执行的命令来记录数据库的状态。
AOF日志对数据库命令的保存顺序是,Redis 先执行命令,把数据写入内存,然后才记录日志。
为什么要后记录日志呢
1、后写,能够避免记录到错误的命令。因为是先执行命令,后写入日志,只有命令执行成功了,命令才能被写入到日志中。
2、避免阻塞当前的写操作,是在命令执行后才记录日志,所以不会阻塞当前的写操作。
AOF 的潜在风险
-
1、如果命令执行成功,写入日志的时候宕机了,命令没有写入到日志中,这时候就有丢失数据的风险了,因为这时候没有写入日志,服务断电之后,这部分数据就丢失了。
这种场景在别的地方也很常见,比如基于 MQ 实现分布式事务,也会出现业务处理成功 + 事务消息发送失败
这种场景,RabbitMQ,RocketMQ,Kafka 事务性,消息丢失和消息重复发送的处理策略
-
2、AOF 的日志写入也是在主线程进行的,如果磁盘的压力很大,写入速度变慢了,会影响后续的操作。
这两种情况可通过调整 AOF 文件的写入磁盘的时机来避免
AOF 文件的写入和同步
AOF 文件持久化的功能分成三个步骤,文件追加(append),文件写入,文件同步(sync)。
AOF 文件在写入磁盘之前是先写入到 aof_buf 缓冲区中,然后通过调用 flushAppendOnlyFile 将缓冲区中的内容保存到 AOF 文件中。
写入的策略通过 appendfsync 来进行配置
-
Always:同步写回 每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;
-
Everysec:每秒写回 每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
-
No:操作系统控制的写回 Redis 不在控制命令的写会时机,交由系统控制。每次写操作命令执行完成之后,命令会被放入到 AOF 文件的内核缓冲区,之后什么时候写入到磁盘,交由系统控制。
RDB 持久化
什么是 RDB 持久化
RDB(Redis database):实现方式是将存在 Redis 内存中的数据写入到 RDB 文件中保存到磁盘上从而实现持久化的。
和 AOF 不同的是 RDB 保存的是数据而不是操作,在进行数据恢复的时候,直接把 RDB 的文件读入到内存,即可完成数据恢复。
RDB 如何做内存快照
Redis 中对于如何备份数据到 RDB 文件中,提供了两种方式
-
1、save: 在主线程中执行,不过这种会阻塞 Redis 服务进程;
-
2、bgsave: 主线程会 fork 出一个子进程来负责处理 RDB 文件的创建,不会阻塞主线程的命令操作,这也是 Redis 中 RDB 文件生成的默认配置;
对于 save 和 bgsave 这两种快照方式,服务端是禁止这两种方式同时执行的,防止产生竞争条件。
Redis 中可以使用 save 选项,来配置服务端执行 BGSAVE 命令的间隔时间
## Save the DB on disk:## save <seconds> <changes>## Will save the DB if both the given number of seconds and the given# number of write operations against the DB occurred.## In the example below the behaviour will be to save:# after 900 sec (15 min) if at least 1 key changed# after 300 sec (5 min) if at least 10 keys changed# after 60 sec if at least 10000 keys changed## Note: you can disable saving completely by commenting out all "save" lines.## It is also possible to remove all the previously configured save# points by adding a save directive with a single empty string argument# like in the following example:## save ""save 900 1save 300 10save 60 10000
save 300 10
就是服务端在300秒,读数据进行了至少10次修改,就会触发一次 BGSAVE 命令
单节点模式
单节点模式下只使用RDB持久化,在某一时刻redis宕机,此时的数据并没有来得及进行rdb持久化备份,这就出现了数据丢失的可能。
在Redis单节点模式下,我们可以可以采用RDB+AOF结合使用防止Redis缓存数据丢失,一个进行全量数据备份,一个进行增量操作指令的备份。
Redis集群模式
在Redis集群主从结构下,如果不开启master的持久化,在master意外宕机后,由于数据全在master节点的内存上,在master节点重启后数据将会全部丢失。此时进行主从数据同步操作,将会导致slave从节点的数据也会被master节点的空数据覆盖清理掉。
在master-slave主从模式下master node 必须开启持久化功能来保证数据不丢失。
但是master-node开启持久化并不意味着数据就不会丢失。
数据异步复制时丢失
因为master-slave节点的数据同步是异步的,可能在数据同步完成前master节点发生宕机,此时这部分的数据就意外丢失了。
脑裂时数据丢失
所谓脑裂,是指master节点自己突然脱离正常网络(并没有宕机,还在运行中),与其他的salve节点的连接断开,但哨兵会认为master节点已经宕机,要从slave节点中重新选举新的master节点,这个时候集群中实际存在两个master节点,这就是脑裂。
虽然某一个slave节点被选举为master节点,但是客户端连接的master仍然是旧master节点,数据还是写向旧master节点上,当旧master节点再次恢复后,会作为slave节点挂在新master节点上,旧master节点中的数据会被新master节点上的数据同步清理掉。
解决方案
通过redis.conf 中配置项,设置至少有1个slave,数据复制和同步的延迟不能超过8秒,来减少异步复制和脑裂导致的数据丢失 :
min-slaves-to-write 1
min-slaves-max-lag 8
如果所有的slave节点的数据同步延时都超过8s,master节点拒绝写请求。
这种情况下脑裂也就最多丢失8秒的数据。