【持久化】这个单词我想大家都不陌生吧。什么是持久化?我们知道,Redis的数据是存储在内存里面的,所以在Redis这里,其实是指把内存中的数据,通过一些策略写到磁盘中,方便因为宕机、或者重启Redis服务的时候,再次把数据加载到内存中。
那么,Redis中持久化策略(方式)有哪些呢?其实主要的方式有如下三种,让我们来看看吧
在默认情况下, Redis 将内存数据库快照保存在名字为 dump.rdb 的二进制文件中。(PS:该持久化策略,是默认的策略,当然不排除在随后的版本中改了)
开启/关闭方式:进入程序的目录,修改
redis.conf
配置文件。开启/关闭RDB只需要将所有的save
保存策略打开/注释掉即可
RDB快照生成的触发方式有两种。一种是通过设置策略,当满足条件的时候自动触发;另一种,当然是手动触发了。
我们先来说一下【自动触发】的方式。自动触发的方式,就是按照Redis提供给我们的语法,在redis.conf
里面增加触发策略。设置规则如下:
语法:
save
[ ...]
解释:在【N 秒内数据集至少有 M 个改动】这一条件被满足时,自动持久化一次
举个例子,设置一条策略【在60秒内有1000个改动时,自动持久化一次】。设置如下
save 60 1000 // 关闭RDB只需要将所有的save保存策略注释掉即可
至于手动触发方式,则是进入redis客户端执行命令save
或bgsave
,就可以生成dump.rdb
文件,每次命令执行都会将所有redis内存快照到一个新的.rdb
文件里,并覆盖原有.rdb
快照文件。
save
是同步执行生成rdb
文件的操作,执行时不会处理外部的命令;bgsave
则是异步执行生成操作,会同时处理外部命令。
Redis借助操作系统的写时复制技术(Copy On Write),在生成快照的同时,依然可以正常处理写命令。简单来说,bgsave
子进程是由主线程fork
出来的,所以可以共享主线程内存的所有数据。bgsave
子进程运行后,开始读取主线程的内存数据,并把他们他们写入.rdb
文件。此时,如果主线程对这些数据也都是读操作,那主线程跟子进程之间肯定没有影响;若此时主线程需要修改一块数据,那么,这块数据会被复制一份,生成该数据的副本。然后bgsave
子进程会把这个副本数据写入.rdb
文件中,而在这个过程中,主线程仍然可以直接修改原来的数据。
save
与bgsave
对比:
命令 | save | bgsave |
---|---|---|
IO类型 | 同步 | 异步 |
是否阻塞redis其他命令 | 是 | 否。不过在生成子进程执行调用函数时会有短暂阻塞 |
复杂度 | O(n) | O(n) |
优点 | 不会消耗额外内存 | 不会阻塞客户端命令 |
缺点 | 阻塞客户端命令 | 需要fork一个子进程,消耗额外内存 |
上面配置的【自动触发】生成.rdb
文件的策略,后台使用的就是bgsave
方式
优点是:由于是二进制文件,所以Redis重启的时候,恢复速度快
缺点是:容易丢失数据,为什么?看下面【AOF】策略的介绍
看了上面的【RDB策略】不知道大家有没有感觉,或者意识到什么。那就是,这种策略其实看起来有点“苛刻”,它的数据安全性并不靠谱!
比如【在60秒内有1000个改动时,自动持久化一次】
的策略之下,万一我在做第1000个改动的时候服务器宕机了,那不是丢掉了前面999个操作了吗?
所以,快照功能并不是非常耐久(durable)的。 如果 Redis 因为某些原因而造成故障停机,那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。不过从1.1版本开始,Redis增加了一种完全耐久的持久化方式: AOF持久化,将修改的每一条指令记录进文件appendonly.aof
文件中(先写入os cache,每隔一段时间fsync到磁盘)。
比如执行命令set zhuge 666
,.aof
文件里会记录如下数据:(这是一种resp协议
格式数据,我在下面写上注释给大家翻译一下什么意思)
*3 # 星号后面的数字表示,执行的命令有多少个参数
$3 # 美元符号后面的数字代表这个参数有几个字符
set
$5 # 美元符号后面的数字代表这个参数有几个字符
zhuge
$3 # 美元符号后面的数字代表这个参数有几个字符
666
注意,如果执行带过期时间的set命令,aof文件里记录的是并不是执行的原始命令,而是记录key过期的时间戳。比如执行set tuling 888 ex 1000
,对应aof文件里记录如下:
*3
$3
set
$6
tuling
$3
888
*3
$9
PEXPIREAT
$6
tuling
$13
1604249786301
开启/关闭方式:进入程序的目录,修改
redis.conf
配置文件。开启/关闭aof只需要修改如下参数:
# appendonly yes
// 有一些版本默认注释掉。打开注释,设置yes
或者no
即可 打开/关闭
开启之后,从现在开始, 每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到.aof
文件的末尾。
这样的话, 当 Redis 重新启动时, 程序就可以通过重新执行.aof
文件中的命令来达到重建数据集的目的。
同样的,我们可以配置Redis多久才将数据同步到磁盘一次。.aof
的触发方式同样也有两种:自动和手动。
【自动触发】的方式,如下:(Redis提供给我们的,需要自己手动打开、关闭)
appendfsync always
:每次有新命令追加到 AOF 文件时就执行一次 fsync ,非常慢,也非常安全。
appendfsync everysec
:每秒 fsync 一次,足够快,并且在故障时只会丢失 1 秒钟的数据。
appendfsync no
:从不 fsync ,将数据交给操作系统来处理。更快,也更不安全的选择。
推荐(并且也是默认)的措施为每秒 fsync 一次
, 这种 fsync 策略可以兼顾速度和安全性(最多丢失最近1秒的缓存数据)。
.aof
文件里可能有太多没用指令,所以【AOF策略】会定期根据内存的最新数据生成aof文件。例如,执行了如下几条命令:
127.0.0.1:6379> incr readcount
(integer) 1
127.0.0.1:6379> incr readcount
(integer) 2
127.0.0.1:6379> incr readcount
(integer) 3
127.0.0.1:6379> incr readcount
(integer) 4
127.0.0.1:6379> incr readcount
(integer) 5
重写后AOF文件里变成:
*3
$3
SET
$2
readcount
$1
5
如下两个配置可以控制AOF自动重写频率:
// aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大
# auto-aof-rewrite-min-size 64mb
// aof文件自上一次重写后文件大小增长了100%则再次触发重写
# auto-aof-rewrite-percentage 100
优点是:数据安全性相对【RDB】方式来说高点
缺点是:恢复速度慢,因为不是二进制,且需要通过【重放】的方式恢复
命令 | RDB | AOF |
---|---|---|
启动优先级 | 低 | 高 |
文件大小 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 容易丢失数据 | 根据策略决定,但整体比较高 |
上面提到的启动优先级什么意思呢?意思是,当Redis启动时,会优先读取.aof
的文件,其次才是.rdb
。为什么呢?因为.aof
文件的数据安全性相对可靠一点啊!
那我该选择哪一种持久化策略呢?其实在生产环境中,可以都启用。反正Redis启动时如果既有.rdb
文件又有.aof
文件的时候,会根据优先级选取。
不出意外,当出现比较特点比较极端的两个方案时,总会有一个折中的方案出现。这就是Redis在4.0之后的版本推出的【混合持久化,AOF + RDB】方式。
重启Redis时,我们很少使用.rdb
来恢复内存状态,因为会丢失大量数据。我们通常使用.aof
日志重放,但是重放.aof
日志性能相对.rdb
来说要慢很多,这样在Redis实例很大的情况下,启动需要花费很长的时间。Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化
PS:混合方式的开启,必须要先开启AOF
开启/关闭方式:进入程序的目录,修改redis.conf
配置文件。开启/关闭需要修改如下参数:
# appendonly yes
# aof-use-rdb-preamble yes // 需要同时上面的参数也为yes才可开启
如果开启了混合持久化,AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存数据的命令存在一起,都写入新的.aof
文件,新的文件一开始不叫appendonly.aof
,等到重写完新的.aof
文件才会进行改名,覆盖原有的.aof
文件,完成新旧两个.aof
文件的替换。
于是在 Redis 重启的时候,可以先加载.aof
文件中的RDB内容,然后再重放增量AOF日志就可以完全替代之前的.aof
全量文件重放,因此重启效率大幅得到提升。
混合持久化AOF文件结构如下:
Redis主从架构模型如下:
由上图可以看出,在主从结构中,通常从节点只做【读】业务,【写】业务通常还是由主节点master完成。并且,它目前并没有我们小白以为的【宕机自动切换新的主节点】的能力。(PS:我以前一直听说什么Redis集群高可用,节点宕机依然不影响业务,所以我乍一看【主从架构】就以为它已经有这个能力了。而事实上并没有,它只是【缓解了节点压力】,并不具备自动切换)
好记性不如多操作几遍
redis主从架构搭建,配置从节点步骤如下:
redis-6380.conf
和redis-6381.conf
,因为我计划搭建【一主二从】的结构redis-6380.conf
为例)# 修改从节点的运行端口
port 6380
# 把pid进程号写入pidfile配置的文件
pidfile /var/run/redis_6380.pid
# 日志文件命名
logfile "6380.log"
# 指定数据存放目录,需要提前在redis-6380.conf的当前目录下,新建好data目录及其下面的slave-80目录
dir ./data/slave-80
# 需要注释掉bind
# bind绑定的是自己机器网卡的ip,如果有多块网卡可以配多个ip,
# 代表允许客户端通过机器的哪些网卡ip去访问,
# 内网一般可以不配置bind,注释掉即可
# bind 127.0.0.1
redis-6380.conf
为例)# 从本机6379的redis实例复制数据,Redis 5.0之前使用slaveof
# 格式:replicaof [master节点的up地址] [master节点的端口6379]
# 比如我的是下面这个
replicaof 127.0.0.1 6379
# 配置从节点只读,默认打开了
replica-read-only yes
# redis-6380.conf文件务必用你复制并修改了之后的redis.conf文件
redis-server redis-6380.conf
redis-cli -p 端口
的方式,连接到对应的从库,校验一下name
,值为helloRedis
,接下来我们去从节点看看:Redis主从工作原理其实并不是那么神奇,主要是保证数据一致性就好了。那该怎么保证呢?首先肯定是要分场景的。比如:第一次过来同步复制(全量同步);之前已经同步过一次了,但后来因为某些原因断了,现在重新连接上,需要继续同步最近的数据(增量同步,断点续传)。
下面再给大家看看【全量同步】跟【增量同步】的业务流程图
PSYNC
命令给master请求复制数据PSYNC
命令后,会在后台进行数据持久化(通过bgsave
生成最新的.rdb
快照文件),持久化期间,master会继续接收客户端的请求,它会把这些可能修改数据集的请求缓存在内存中.rdb
文件数据集发送给slave,slave会把接收到的数据进行持久化生成.rdb
,然后再加载到内存中小总结:
上面在生成持久化文件的时候有两个要点不知道大家注意到没有?那就是:bgsave
跟.rdb
。
前面的bgsave
比较好理解,异步生成.rdb
文件嘛,为了不阻塞主节点的客户读写。那为什么是.rdb
而不是.aof
呢?其实说来也不算难理解,只不过我估计大家刚接触【redis持久化】所以比较陌生而已。因为.rdb
恢复速度快啊!二进制文件嘛。
PSYNC
去master同步数据,slave与master能够在网络连接断开重连后只进行部分数据复制(断点续传)offset
和master的进程id什么是主从复制风暴?简单来说,就是一个主节点,需要应付很多从节点的复制请求,就算是采用异步执行同步命令,但是当数据多了之后也会容易陷入瓶颈。这就是主从复制风暴。
为了缓解主从复制风暴(多个从节点同时复制主节点导致主节点压力过大),可以做如下架构,让部分从节点与从节点(与主节点同步)同步数据: