1.持久化
一提到持久化,我们就会第一时间联想到MySQL的事务,MySQL的事务有4个比较核心的特征:原子性(把多个操作打包成一个整体),一致性(事务执行之前和之后,数据都不能离谱),持久性(事务中做出的修改都会保存在硬盘上),隔离性(事务并发执行,涉及一系列的问题)
Redis是一个内存数据库,数据存储在内存中,内存中的数据是不持久的,要想做到持久,就需要让Redis把数据存储到硬盘上。Redis决定内存中存储数据(快!),硬盘中也存数据(持久化)。当要插入一个新的数据时,就需要把这个数据写入到内存和硬盘上,当查询某个数据的时候,直接从内存中读取,硬盘中的数据知识在Redis重启的时候,用来恢复内存中的数据,但代价是消耗更多的空间,毕竟一份数据存两遍
持久化可以简单理解为数据存储在硬盘上,Redis支持RDB和AOF两种持久化机制,持久化功能有效地避免因进程退出造成数据丢失的问题,当下次重启时利用之前持久化生成的文件即可实现数据的恢复
2.RDB
RDB(Redis DataBase)持久化就是把当前进程中的数据生成“快照”保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发,Redis会定期给内存中当前存储的这些数据,拍成一个“快照”,生成一个文件存储在硬盘中,后续Redis一旦重启,内存数据就没了,可以根据“快照”(文件)把内存中的数据给恢复回来
2.1 触发机制
2.1.1 手动触发
程序员通过Redis客户端,执行特殊的命令,来触发生成快照。手动触发分别对应save和bgsave命令:
save命令:阻塞当前Redis服务器,直到RDB过程完成为止,对于内存比较大的实例造成长时间阻塞,基本不采用
bgsave命令:Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责(不会影响Redis服务器处理其他客户端的请求和命令),完成后自动结束。阻塞只发生在fork阶段,一般时间很短。Redis内部所有设计RDB的操作都采用类似bgsave的方式
2.1.2 自动触发
除了手动触发外,Redis运行自动触发(Redis配置文件中)RDB持久化,这个触发机制才是比较有价值的
1.使用save配置。如“save m n”表示m秒内数据集发生n次修改,自动触发RDB持久化
2.从节点进行全量复制时,主节点自动进行RDB持久化,随后将RDB文件发送给从节点
3.执行shutdown命令关闭Redis时,执行RDB持久化
2.2 流程说明
bgsave是主流的RDB持久化方式
bgsave命令的运行流程:
1.执行bgsave,判断当前是否已经存在其他正在工作的子进程,如果有其他进程正在bgsave,此时就直接把当前的bgsave返回
2.如果没有其他子进程,就通过fork这样的系统调用创建一个子进程来,fork过程中父进程会阻塞,可以通过info stats命令查看lastest_fork_usec选项,可以获取最近一次fork操作的耗时,单位为微秒
3.父进程完成fork后,bgsave命令返回“Background saving started”信息并不再阻塞父进程,生成“快照”的过程中父进程继续接受客户端的请求,继续提供服务
4.子进程创建RDB文件,根据父进程内存生产临时快照文件,完成后对原有文件进行原有文件进行原子替换。执行lastsave命令可以获取最后一次生成RDB的时间,对应info统计rdb_last_save_time选项
5.子进程发送信号给父进程表示完成,父进程更新统计信息
fork是Linux系统提供的一个创建子进程的api(系统调用),fork创建子进程简单粗暴,直接把当前(父进程)复制一份,作为子进程。一旦复制完成,父子进程就是两个独立的进程,各自执行各自的任务
fork复制的过程中,会复制pcb,虚拟地址空间(内存中的数据),文件描述符表。本来Redis Server中有若干变量保存了一些键值对,随着这样的fork进行,子进程的这个内存空间也会存在和父进程中一模一样的数据。接下来安排子进程去执行持久化操作,也就相当于把父进程本体这里的内存数据给持久化了。父进程打开一个文件,fork之后,子进程同样可以使用该文件,也就导致了子进程持久化写入的那个文件和父进程本来要写的文件是同一个
这个时候就会有人问了,如果当前Redis服务器中存储的数据特别多,内存消耗特别大,进行上述复制操作,是否会有很大的性能开销?
此处的性能开销其实挺小的。fork在进行内存拷贝时,不是简单无脑将所有元素复制一份,而是通过“写时拷贝”的机制完成的。如果父进程和子进程的内存数据完全相同,此时不会触发真正的拷贝动作(父子使用一份内存),但是这两个进程的内存空间应该是各自独立的。一旦某一进程对内存数据做了修改,就会触发真正的物理内存上的数据拷贝。在进行bgsave这个场景中绝大部分的内存是不需要修改的,因此子进程的“写时拷贝”并不会出发很多次,也保证了整体拷贝的时间是可控的,高效的
2.3 RDB文件的处理
2.3.1 保存
保存:RDB文件保存在dir配置指定的目录(默认/var/lib/redis/)下,文件名通过dbfilename配置(默认dump.rdb)指定。可以通过执行config set dir {newDir}和config set dbfilename {newFilename}运行期间动态执行,当下次运行RDB文件会保存到新的目录。当生成RDB镜像操作的时候,此时就会生成“快照”数据,先保存在一个临时的文件中,当“快照”生成完毕之后,再删除之前的rdb文件,把新生成的临时rdb文件改为dump.rdb。自始至终,rdb文件始终只有一个
2.3.2 压缩
压缩:Redis默认采用LZF算法对生成的RDB文件做压缩处理,压缩处理后的文件远远小于内存大小,默认开启,可以通过参数config set rdbcompression {yes|no}动态修改。虽然压缩RDB文件会消耗CPU,但可以大幅度降低文件的体积,方便保存到硬盘或通过网络发送到从节点,因此建议开启
2.3.3 校验
校验:如果Redis启动时加载到损坏的RDB文件会拒绝启动。这是可以使用Redis提供的redis-check-dump工具检测RDB文件并获取对应的错误报告
2.4 RDB文件的优缺点
优点:
RDB是紧凑压缩的二进制文件,代表Redis在某个时间点上生成的数据快照。非常适用于备份,全量复制等场景。把RDB文件复制到远程机器或者文件系统中(如hdfs)用于灾备。Redis加载RDB恢复数据非常快(二进制)
缺点:
RDB方式持久化数据无法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork创建子进程,俗语重量级操作,频繁执行成本过高。RDB文件使用特定的二进制格式保存,Redis版本严谨过程中有多个RDB版本,兼容性可能有风险