同步操作将从 古春波/java-construct 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
Redis
的数据全部存储在内存中,如果突然宕机,数据就会全部丢失,因此必须有一套机制来保证 Redis
的数据不会因为故障而丢失,这种机制就是Redis
的持久化机制,它会将内存中的数据库状态保存到磁盘 中。
RDB
持久化是把当前进程数据生成快照保存到硬盘的过程,Redis
快照是最简单的持久化方式,当满足特定条件时,它将生成数据集的时间点快照,例如,先前的快照是在2分钟前创建的,并且创建的两分钟后又有了200次的新写入,则将创建一个新的写照。此条件可以由用户配置 Redis
实例来控制,也可以在运行时修改而无需重新启动服务器。快照用于包含整个数据集的单个 .rdb
文件生成。
触发RDB
持久化过程分为手动触发和自动触发。
手动触发分别对应save
和bgsave
命令:
save
命令:阻塞当前Redis
服务器,直到RDB
过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上环境不建议使用。运行save
命令对应的Redis
日志如下:DB saved on disk
bgsave
命令:Redis
进程执行fork
操作创建子进程,RDB
持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork
阶段,一般时间很短。运行bgsave
命令对应的Redis
日志如下:
Background saving started by pid 3151
DB saved on disk
RDB: 0 MB of memory used by copy-on-write
Background saving terminated with success
显然bgsave
命令是针对save
阻塞问题做的优化。因此Redis
内部所有的涉及RDB
的操作都采用bgsave
的方式,而save
命令已经废弃。
除了执行命令手动触发之外,Redis
内部还存在自动触发RDB
的持久化机制,例如以下场景:
bgsave
生成RDB
文件发送给从节点。(以后将复制的时候会讲到)。debug reload
命令重新加载Redis
时,也会自动触发save
操作。shutdown
命令时,如果没有开启AOF
持久化功能则自动执行bgsave
。快照持久化是Redis
默认采用的持久化方式,在redis.conf
配置文件中默认有此下配置:
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
执行bgsave
命令之后,Redis
父进程会判断是否有正在执行的RDB/AOF
子进程正在执行,如果发现了有,则bgsave
命令直接返回。
父进程执行fork
操作创建子进程,fork
命令执行过程中父进程会阻塞。
因为存在操作系统多进程 COW(Copy On Write) 机制。
Redis
在持久化时会调用glibc
的函数fork
产生一个子进程,简单理解也就是基于当前进程复制了一个进程,主进程和子进程会共享内存里面的代码块和数据段:所以快照持久化可以完全交给子进程来处理,父进程则继续处理客户端请求。子进程做数据持久化,它不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是父进程不一样,它必须持续服务客户端请求,然后对内存数据结构进行不间断的修改。
这个时候就会使用操作系统的**
COW
机制来进行数据段页面**的分离。数据段是由很多操作系统的页面组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改(因此,避免在大量写入时做子进程重写操作,这样将导致父进程维护大量页副本,造成内存消耗)。这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据。
父进程fork
完成后,bgsave
命令返回“Background saving started
”信息并不再阻塞父进程,可以继续响应其他命令。
子进程会创建RDB
文件,根据父进程内存生成临时快照文件,完成后对原有文件进行替换。
进程发送信号给父进程表示完成,父进程更新统计信息。
保存:RDB
文件保存在配置文件中的dir
配置指定的目录下,文件名通过配置文件中的dbfilename
配置指定。可以通过执行config set dir{newDir}
和config setdbfilename{newFileName}
命令在运行期动态执行,当下次运行时RDB
文件会保存到新目录。
压缩:Redis
默认采用LZF
算法对生成的RDB
文件做压缩处理,压缩后的文件远远小于内存大小,默认开启,可以通过参数config setrdbcompression{yes|no}
动态修改。虽然压缩RDB
会消耗CPU
,但可大幅降低文件的体积,方便保存到硬盘或通过网络发送给从节点,因此线上建议开启。
校验:如果Redis
加载损坏的RDB
文件时拒绝启动,并打印如下日志:# Short read or OOM loading DB. Unrecoverable error, aborting now
,这时可以使用Redis
提供的redis-check-dump
工具检测RDB
文件并获取对应的错误报告。
RDB
的优点:
RDB
是一个紧凑压缩的二进制文件,代表Redis
在某个时间点上的数据快照。非常适用于备份,全量复制等场景。比如每6小时执行bgsave
备份,并把RDB
文件拷贝到远程机器或者文件系统中(如hdfs
),用于灾难恢复。Redis
加载RDB
恢复数据远远快于AOF
的方式。RDB
的缺点:
RDB
方式数据没办法做到实时持久化/秒级持久化。因为bgsave
每次运行都要执行fork
操作创建子进程,属于重量级操作,频繁执行成本过高。RDB
文件使用特定二进制格式保存,Redis
版本演进过程中有多个格式的RDB
版本,存在老版本Redis服务无法兼容新版RDB
格式的问题。RDB
快照不是很持久。如果运行 Redis
的计算机停止运行,电源线出现故障或者您 kill -9
的实例意外发生,则写入 Redis
的最新数据将丢失。尽管这对于某些应用程序可能不是什么大问题,但有些使用案例具有充分的耐用性,在这些情况下,快照并不是可行的选择。因此就有了AOF
持久化方式来解决这个问题。
AOF
(append only file
)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF
文件中的命令达到恢复数据的目的。目前已经是Redis
持久化的主流方式。
开启AOF
功能需要在配置文件配置:appendonly yes
,默认不开启。AOF
文件名通过appendfilename
配置设置,默认文件名是appendonly.aof
。保存路径同RDB
持久化方式一致,通过dir
配置指定。AOF
的工作流程操作:命令写入(append
)、文件同步(sync
)、文件重写(rewrite
)、重启加载(load
)
aof_buf
(缓冲区)中。AOF
缓冲区根据对应的策略向硬盘做同步动作。AOF
文件越来越大,需要定期对AOF
文件进行重写,达到压缩的目的。Redis
服务器重启时,可以加载AOF
文件进行数据恢复。了解AOF
工作流程之后,下面针对每个步骤做详细介绍。命令写入(append
)、文件同步(sync
)、文件重写(rewrite
)、重启加载(load
)
AOF
命令写入的内容直接是文本协议格式。例如set hello world
这条命令,在AOF
缓冲区会追加如下文本:3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
下面介绍关于AOF
的两个疑惑:
AOF
为什么直接采用文本协议格式?可能的理由如下:
AOF
之后,所有的命令都是用文本协议格式进行追加操作,避免了二次处理开销AOF
为什么把命令追加到aof_buf
中?
Redis
使用单线程响应命令,如果每次写AOF
文件命令都直接追加到硬盘,那么性能完全取决于当前硬盘负载。先写入缓冲区aof_buf
中,还有另一个好处,Redis
可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。我们接着刚刚提到的缓冲区同步硬盘策略,Redis
提供了多种AOF
缓冲区同步文件策略,由参数appendfsync
控制,不同值的含义如下表所示:
always
时,每次写入都要同步AOF
文件,在一般的SATA
硬盘上,Redis
只能支持大约几百TPS
写入,显然跟Redis
高性能特性背道而驰,不建议配置。no
,由于操作系统每次同步AOF
文件的周期不可控,而且会加大每次同步硬盘的数据量,虽然提升了性能,但数据安全性无法保证。everysec
,是建议的同步策略,也是默认配置,做到兼顾性能和数据安全性。理论上只有在系统突然宕机的情况下丢失1秒的数据。系统调用
write
和fsync
说明:
write
操作会触发延迟写(delay write
)机制,就是Linux
在内核提供页缓冲区用来提高硬盘IO
性能。write
操作写入这个页缓冲区之后就直接返回了,认为完成了这次的write
操作,但是这个实际还没有真正地写到硬盘上,同步硬盘操作依赖于系统调度机制,例如:缓冲区页空间写满或达到特定时间周期。同步文件到硬盘之前,如果此时系统故障宕机,缓冲区内数据将丢失。fsync
针对单个文件操作(比如AOF
文件),做强制硬盘同步,fsync
将阻塞直到写入硬盘完成后返回,保证了数据持久化。
AOF
追加阻塞问题,我们对everysec
同步策略进行进一步的讨论
当开启AOF
持久化时,常用的同步硬盘的策略是everysec
,用于平衡性能和数据安全性。对于这种方式,Redis
使用另一条线程每秒执行fsync
同步硬盘。当系统硬盘资源繁忙时,会造成Redis
主线程阻塞。
阻塞流程分析:
AOF
缓冲区。AOF
线程负责每秒执行一次同步磁盘操作,并记录最近一次同步时间。AOF
同步时间:
通过对AOF
阻塞流程可以发现两个问题:
everysec
配置最多可能丢失2秒数据,不是1秒。fsync
缓慢,将会导致Redis
主线程阻塞影响效率。AOF
阻塞问题定位:
AOF
阻塞时,Redis
输出如下日志,用于记录AOF fsync
阻塞导致拖慢Redis
服务的行为:Asynchronous AOF fsync is taking too long (disk is busy). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis
AOF
追加阻塞事件发生时,在info Persistence
统计中,aof_delayed_fsync
指标会累加,查看这个指标方便定位AOF
阻塞问题。AOF
同步最多允许2秒的延迟,当延迟发生时说明硬盘存在高负载问题,可以通过监控工具如iotop
,定位消耗硬盘IO
资源的进程。随着命令不断写入AOF
,文件会越来越大,为了解决这个问题,Redis
引入AOF
重写机制压缩文件体积。AOF
文件重写是把Redis
进程内的数据转化为写命令同步到新AOF
文件的过程。
重写后的AOF
文件为什么可以变小?有如下原因:
AOF
文件含有无效命令,如del key1
、hdel key2
、srem keys
、seta111
、set a222
等。重写使用进程内数据直接生成,这样新的AOF
文件只保留最终数据的写入命令。lpush list a
、lpush list b
、lpush list c
可以转化为:lpush list a b c
。为了防止单条命令过大造成客户端缓冲区溢出,对于list、set、hash、zset
等类型操作,以64个元素为界拆分为多条。AOF
重写降低了文件占用空间,除此之外,另一个目的是:更小的AOF
文件可以更快地被Redis
加载。AOF
重写过程可以手动触发和自动触发:·手动触发:直接调用bgrewriteaof
命令。自动触发:根据auto-aof-rewrite-min-size
(表示运行AOF重写时文件最小体积,默认为64MB)和auto-aof-rewrite-percentage
(代表当前AOF
文件空间(aof_current_size
)和上一次重写后AOF
文件空间(aof_base_size
)的比值。)参数确定自动触发时机。
自动触发时机=aof_current_size
>auto-aof-rewrite-min-size
&&(aof_current_size-aof_base_size
)/aof_base_size
>=auto-aof-rewrite-percentage
其中aof_current_size
和aof_base_size
可以在info Persistence
统计信息中查看。
当触发AOF
重写时,内部做了哪些事呢?
AOF
重写请求。如果当前进程正在执行AOF
重写,请求不执行并返回如下响应:ERR Background append only file rewriting already in progress
;如果当前进程正在执行bgsave
操作,重写命令延迟到bgsave
完成之后再执行,返回如下响应:Background append only file rewriting scheduled
fork
创建子进程,开销等同于bgsave
过程。fork
操作完成后,继续响应其他命令。所有修改命令依然写入AOF
缓冲区并根据appendfsync
策略同步到硬盘,保证原有AOF
机制正确性。fork
操作运用写时复制技术,子进程只能共享fork
操作时的内存数据。由于父进程依然响应命令,Redis
使用“AOF
重写缓冲区(aof_rewrite_buf)”保存这部分新数据,防止新AOF
文件生成期间丢失这部分数据。AOF
文件。每次批量写入硬盘数据量由配置aof-rewrite-incremental-fsync
控制,默认为32MB,防止单次刷盘数据过多造成硬盘阻塞。AOF
文件写入完成后,子进程发送信号给父进程,父进程更新统计信息,具体见info persistence
下的aof_*
相关统计。AOF
**重写缓冲区(aof_rewrite_buf)**的数据写入到新的AOF
文件。AOF
文件替换老文件,完成AOF
重写。AOF
和RDB
文件都可以用于服务器重启时的数据恢复
重启 Redis
时,我们很少使用 rdb
来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF
日志重放,但是重放 AOF
日志性能相对 rdb
来说要慢很多,这样在 Redis
实例很大的情况下,启动需要花费很长的时间。
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 rdb
文件的内容和增量的 AOF
日志文件存在一起。这里的 AOF
日志不再是全量的日志,而是 自持久化开始到持久化结束 的这段时间发生的增量 AOF
日志,通常这部分 AOF
日志很小:
于是在 Redis
重启的时候,可以先加载 rdb
的内容,然后再重放增量 AOF
日志就可以完全替代之前的 AOF
全量文件重放,重启效率因此大幅得到提升。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。