【redis】什么是持久化之 RDB
什么是持久化
MySQL 的事务,有四个比较核心的特性:
- 原子性
- 一致性
- 持久性==>持久化(说的一回事)
- 把数据存储在硬盘上==>持久
- 把数据存在内存上==>不持久
- 重启进程/重启主机之后,数据是否还存在
- 隔离性
Redis
是一个内存数据库,是把数据存在内存中的。内存中的数据并不是持久的,要想能做到持久,就需要让 Redis
把数据存储在硬盘上
Redis
相比于 MySQL
这样的关系型数据库,最明显的有点/优势==>效率高/快
- 因为它的数据是存在内存上的
如何实现持久化
为了保证速度快,数据肯定还得再内存中,但是为了持久,数据还得想办法存储在硬盘上
redis
决定,内存和硬盘上都存数据- 这样的两份数据,理论上是完全相同的(实际上可能存在一个小的概率有差异,取决于我们具体怎么进行持久化)
- 当要插入一个新的数据的时候,就需要把这个数据,同时写入内存和硬盘。
- 实际上怎么写入硬盘,还有不同的策略,不会导致每次都要写两份,导致效率降低
- 当查询某个数据的时候,直接从内存读取
- 硬盘的数据只是在
redis
重启的时候,用来恢复内存中的数据的
这样就既能保证高效,又能通过硬盘恢复数据,保证持久化的效果,代价就是消耗了更多空间(一份数据,存了两遍)(硬盘便宜,不会带来太多成本)
实现策略
RDB
-->Redis DataBase
AOF
-->Append Only FIle
RDB
定期的把我们 Redis
内存中的数据,都给写入硬盘中,生成一个“快照”
Redis
给内存中当前存储的这些数据,赶紧拍个照片,生成一个文件,存储在硬盘中- 后续
Redis
一旦重启了(内存数据就没了),就可以根据刚才的“快照”,把内存中的数据给恢复回来
[!quote] 快照
某个案发现场,警察来了之后,会拉上警戒线,然后开始忙碌地拍照,记录现场==>后续就可以根据这些记录的照片,来还原出现场当前发生了什么
“定期”具体又分为两种方式:
- 手动触发
- 程序员通过
Redis
客户端,执行特定的命令,来触发快照生成(save
、bgsave
)
- 程序员通过
- 自动触发
- 在
Redis
配置文件中,设置一下,让Redis
每隔多长时间/每产生多少次修改就出发
- 在
手动触发
执行 save
的时候,Redis
就会全力以赴的进行“快照生成”操作,此时就会阻塞 Redis
的其他客户端的命令
- 类似于
keys *
的后果 - 一般不建议使用
save
是在前台,bgsave
(background
)是在后面偷摸进行,不会影响 Redis
服务器处理其他客户端的请求和命令
- 这样既可以保证持久化
- 又可以保证
Redis
可以正常去响应命令
bgsave
Redis
是怎么做到 bgsave
的呢?是不是偷偷搞了个多线程?
- 并非如此,多线程是实现并发编程的场景的一种方式,但不是唯一
Redis
使用的是“多进程”的方式,来完成的并发编程,实现bgsave
的
当 Redis
服务器(父进程)收到 bgsave
的命令之后,首先会进行一个判断
1 . 判定当前是否已经存在其他正在工作的子进程
比如现在已经有一个子进程正在执行 bgsave
,此时就直接把当前的 bgsave
返回
2 . 如果没有其他的工作子进程,就通过 fork
这样的系统调用,创建一个子进程来
fork
是 Linux
系统提供的一个创建子进程的 API
- 如果是其他系统,比如
Windows
,创建子进程就不是fork
(CreatProcess
) fork
创建子进程,简单粗暴,就是直接把当前的进程(父进程)复制一份,作为子进程。一旦复制完成了,父子进程就是两个独立的进程,就格子执行各自的了- 会复制 PCB、虚拟地址空间(内存中的数据)、文件描述附表…
- 本来
redis server
中,有若干变量,保存了一些键值对数据。随着这样的fork
的进行,子进程的这个内存里也会存在和刚才父进程中一模一样的变量
因此,复制出来的“克隆体“(子进程),内存中的数据就是和“本体”(父进程)是一样的。接下来安排子进程去进行“持久化”操作,也就相当于把父进程本体这里的数据给持久化了
父进程打开了一个文件,
fork
了之后,子进程也是可以同时使用这个文件的
- 导致了子进程持久化写入的那个文件,和父进程本来要写的文件是同一个
如果当前 redis
服务器中存储的数据特别多,内存消耗特别大,此时进行上述的复制操作,是否会有很大的性能开销?
- 此处的性能开销其实挺小
fork
在进行内存拷贝的时候,不是简单无脑的直接把所有的数据都拷贝一遍,而是“写实拷贝”的机制来完成的- 如果子进程里的这个内存数据,和父进程的内存数据完全一样,此时就不会触发真正的拷贝动作(而是爷俩其实用一份内存数据)
- 但是,其实这俩进程的内存空间,应该是各自独立的。一旦某一方针对这个数据进行了修改,就会立即触发真正的物理内存上的数据拷贝
在进行 bgsave
这个场景中,绝大部分的内存数据,是不需要进行改变的(整体来说这个过程执行的还挺快,这个短时间内,父进程中不会有大批的内存数据变化)
因此,子进程的“写实拷贝”不会触发很多次,也就保证了整体的“拷贝时间”是可控的,高效的
- 子进程负责进行写文件,生成快照的过程,父进程继续接收客户端的请求,继续正常提供服务
- 子进程完成整体的持久化过程之后,就会通知父进程干完了,父进程就会更新一些统计信息,子进程就可以结束销毁了
总结:
创建子进程,子进程完成持久化操作,持久化会把数据写入到新的文件中,然后使用新的文件替换旧的文件
- 子进程不好观察,我们观察新旧文件(通过
stat
命令,查看文件的inode
号)
RDB 文件
redis
生成的 RDB
文件,是放在 redis
的工作目录中的,也是在 redis
配置文件中进行设置的
RDB
机制生成的镜像文件,redis
服务器默认就是开启了RDB
的- 这是一个二进制文件,把内存中的数据,以压缩(消耗一定 CPU 资源,但是能省空间)的形式,保存到这个二进制文件中
- 最多打开看看就行了,不要乱改,一旦要是把数据的格式改坏了,就麻烦了
- 后续
redis
服务器重新启动,就会尝试加载这个RDB
文件,如果发现格式错误,就可能会加载数据失败
redis
提供了 RDB
文件的检查工具
当执行生成 RDB
镜像操作的时候,此时就会把要生产的快照数据,先保存到一个临时文件中。当这个快照生成完毕之后,再删除之前的 RDB
文件,把新生成的临时的 RDB
文件名字改成刚才的 dump.rdb
- 自始至终,
RDB
文件始终只有一个
效果
RDB
文件中的数据,不是你这边插入了数据,就会立即更新的
- 插入数据之后,观察
RDB
文件内容 - 发现没有变化,此时我们执行保存数据的命令
可以发现 RDB
文件内容更新了
RDB
的触发时机:
- 手动(save、bgsave)
- 自动(配置文件中,进行设置)
- 在
900s
之后,并且至少存在一次key
的修改,就会触发自动更新
这些数值都是可以自由修改的,但是此处修改数据的时候,有一个基本的原则:
- 生成一次
RDB
快照,这个成本较高,不能让这个操作执行太频繁
redis
持久化生成快照操作,不仅仅是手动执行命令才出发,也可以自动触发
- 通过刚才配置文件中
save
执行M
时间内,修改N
次 - 通过
shutdown
命令(redis
里的一个命令)关闭redis
服务器,也会触发(service redis-server restart
)(正常关闭) redis
进行主从复制的时候,主节点也会自动生成RDB
快照,然后把RDB
快照文件内容传输给从节点
- 如果是通过正常流程重新启动
redis
服务器,此时redis
服务器会在退出的时候,自动触发生成RDB
操作- 但如果是异常重启(
kill -9
或者服务器掉电),此时redis
服务器来不及生成RDB
,内存中尚未保存到快照中的数据,就会随着重启而丢失
- 若要修改
RDB
配置,修改完成后需要重启服务器- 如果
RDB
文件被改坏了,redis
服务器启动不了了,我们可以去看一下日志,查看报错信息
redis
提供了 RDB
文件的检查工具,可以先通过检查工具,检查一下 RDB
文件格式是否符合要求
- 检查工具和
reids
服务器,在5.0
版本是同一个可执行程序,但是我们可以在运行的时候加上不同选项 - 在运行的时候,加上
RDB
文件作为命令行参数,此时就是以检查工具的方式来运行
- 这里就显示
RDB
文件里面有错误
RDB 小结
- RDB 是一个紧凑压缩的二进制文件,代表
redis
在某个时间点上的数据快照。非常适用于备份,全量复制等场景。比如每 6 小时执行bgsave
备份,并把RDB
文件复制到远程机器或者文件系统中(如hdfs
)用于灾备 redis
加载RDB
恢复数据远远快于AOF
的方式RDB
是使用二进制的方式来组织数据,直接把数据读取到内存中,按照字节的格式取出来,放到结构体/对象中即可AOF
是使用文本的方式来组织数据
RDB
方式数据没办法做到实时持久化,因为bgsave
每次运行都要执行fork
创建子进程,属于重量级操作,频繁执行成本过高RDB
文件使用特定二进制格式保存,redis
版本演进过程中有多个RDB
版本,兼容性可能有风险- 老版本的
redis
的RDB
文件,放到新版本的redis
中不一定能识别
- 老版本的
如果确实需要有一些“升级版本”的需求,就可以通过写一个程序的方式,直接遍历旧的
redis
中的所有key
,把数据取出来,插入到新的redis
服务器中即可