1. 浅谈持久化
持久化:能够在重启主机/进程的时候,将数据从硬盘中恢复到内存的特性。
持久化相信大家都是不陌生的,毕竟MySQL中事务ACID四大特性中就包含持续性这样的特点,所谓持久化,本质上就是将数据保存在硬盘上;不持久化就意味着将数据保存在内存中。
Redis的持久化机制:对于Redis而言,将数据保存在内存中是其效率高的重要原因,但是为了保证数据的可靠性,Redis也提供了持久化机制。那么问题来了,Redis的持久化机制会影响到Redis的性能吗?大概率是不会的,因为Redis查询数据的时候仍然是从内存中读取,只是额外将数据多保存了一份在硬盘上,额外内存开销也没有很大,Redis具体提供了RDB、AOF两种机制进行持久化。
2. RDB机制
RDB(Redis DataBase):简单来说,Redis会定期的将内存的全量数据生成一个快照(dump.rdb文件),然后保存在硬盘中。
这个快照就类似于警察到达案发现场后,就会在周围设立警戒线,然后拍照保存现场状态。当Redis遇到特殊情况(断电)进程异常退出后,尽管内存数据丢失,仍旧可以通过这个快照文件进行恢复
2.1 RDB触发时机
RDB的触发时机有两种情况:
- 手动触发:即通过客户端命令的方式执行生成快照的过程
save
命令:当执行save命令后,redis服务器就会全力以赴的生成这个RDB快照文件,进而阻塞其余redis客户端命令的执行(不推荐×)bgsave
命令:即通过后台方式运行,此处redis服务器会fork出一个子进程,然后由子进程完成生成RDB快照的任务,此时主进程仍然可以处理其余的redis客户端命令
- 自动触发:在redis的配置文件(redis.conf)配置经过多长时间 / 经过多少次修改。
关于RDB快照文件的存储位置在redis的配置文件(redis.conf)中有明确定义:
此处我的云服务器默认dump.rdb
文件就生成在./
的工作目录下,当我们使用vim dump.rdb
查看这个文件
我们观察该文件的格式,可以发现该RDB文件是一个二进制文件,后续redis重启后,就会尝试该文件,如果格式出现错误,就有可能会出现加载失败!
由于dump.rdb文件非常重要,因此不要随意修改!redis还提供了一些客户端命令例如redis-check-rdb等用于检查rdb文件是否正确
总结:
- redis默认开启了RDB策略,会自动生成
dump.rdb
快照文件,该文件路径可以在配置文件redis.conf
目录进行配置 - dump.rdb文件是一个二进制压缩文件
- 可以通过手动触发/自动触发两种方式来生成RDB镜像文件
2.2 bgsave执行流程
当redis服务器处理bgsave
命令就会执行以下流程:
执行流程:
- 服务器接收
bgsave
命令 - 服务器主进程就会fork出一个子进程,此时主进程可以继续执行其余客户端命令
- 子进程具有和父进程相同的内存数据,就可以依据这些数据生成rdb二进制文件(存放在一个临时文件中),处理完后才会将新的临时文件覆盖原先的rdb文件
- 当子进程任务完成之后就会使用信号机制通知父进程
2.3 RDB自动触发策略
由于rdb文件并不是实时更新的,因此我们还需要查看redis.conf
配置的触发条件:
上述配置项就配置了RDB的触发策略:save 经过时间 修改次数
,因此上面三项配置就是:
- 在3600s内修改次数达到1次,就会在3600s(1小时)生成RDB文件
- 在360s内修改次数达到100次,就会在300s(5分钟)生成RDB文件
- 在60s内修改次数达到10000次,就会在60s(1分钟)生成RDB文件
事实上我们可以在配置项中任意更改,但需要满足一定条件:生成RDB的频率不能太高,因为生成一次RDB快照需要将内存中的全量数据全部压缩,成本比较大
RDB的缺点:正是因为RDB采用的是每隔一定时间进行RDB快照生成,例如如果配置为save 5 1,那么当前执行过一次RDB策略,但是接下来5min之类有大量请求涌入,就在某一刻,系统断电了,此时RDB文件没有进行更新,导致了数据不一致的情况
2.4 实操
理论大致讲完了,现在我们进行实操验证:
场景一:手动执行save/bgsave生成快照
127.0.0.1:6379> set k1 111
OK
127.0.0.1:6379> set k2 222
OK
127.0.0.1:6379> set k3 333
OK
127.0.0.1:6379> bgsave
Background saving started
当我们尝试强制杀死redis进程的时候,重启redis服务,观察能否恢复内存数据:
[root@VM-16-5-opencloudos redis]# ps -aux | grep 6379
redis 672 0.1 0.2 136452 5108 ? Ssl Jun11 6:39 /www/server/redis/src/redis-server 0.0.0.0:6379
root 2667081 0.0 0.1 6792 2048 pts/0 S+ 14:58 0:00 grep --color=auto 6379
[root@VM-16-5-opencloudos redis]# kill -9 672
[root@VM-16-5-opencloudos redis]# systemctl restart redis
[root@VM-16-5-opencloudos redis]# redis-cli
127.0.0.1:6379> keys *
1) "k3"
2) "k1"
3) "k2"
可以看到内存数据已经被恢复到内存中了!
场景二:插入新数据后不执行bgsave命令
127.0.0.1:6379> keys *
1) "k3"
2) "k1"
3) "k2"
127.0.0.1:6379> set k4 444
OK
127.0.0.1:6379> exit
[root@VM-16-5-opencloudos redis]# ps -aux | grep redis
redis 2667513 0.1 0.6 136452 11640 ? Ssl 14:59 0:01 /www/server/redis/src/redis-server 0.0.0.0:6379
root 2673606 0.0 0.1 6792 2048 pts/0 S+ 15:11 0:00 grep --color=auto redis
[root@VM-16-5-opencloudos redis]# kill -9 2667513
[root@VM-16-5-opencloudos redis]# systemctl restart redis
[root@VM-16-5-opencloudos redis]# redis-cli
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
此时就可以证明redis是根据dump.rdb
快照文件恢复数据的,但是其中没有新添加的k4信息,因此不会恢复到内存中
这里需要注意:我们必须要通过kill -9的方式杀死进程,如果使用一些例如systemctl stop redis等命令,redis会在shutdown之前进行生成RDB的过程,如下几种方式都可以生成RDB快照
- 执行save/bgsave/自动触发
- 进行主从复制
- 执行shutdown等命令
场景三:观察执行bgsave文件替换过程
前面我们提到过,当执行bgsave命令时子进程会将数据文件存放到临时文件中,然后再替换原有的RDB文件,我们可以通过Linux文件系统的stat命令查看文件inode编号:
[root@VM-16-5-opencloudos redis]# stat dump.rdb
File: dump.rdb
Size: 2151 Blocks: 8 IO Block: 4096 regular file
Device: 252,1 Inode: 829479 Links: 1
[root@VM-16-5-opencloudos redis]# redis-cli
127.0.0.1:6379> bgsave
Background saving started
127.0.0.1:6379> exit
[root@VM-16-5-opencloudos redis]# stat dump.rdb
File: dump.rdb
Size: 2151 Blocks: 8 IO Block: 4096 regular file
Device: 252,1 Inode: 819717 Links: 1
可以发现dump.rdb文件的编号已经发生了改变
补充:在Linux上常见的文件系统组织方式(ext4)将整个文件系统分为了以下三大部分:
- 超级块(存放一些管理信息)
- inode区(存放inode节点,每个文件都会被分配一个inode数据结构,存放各种元数据信息)
- block区(存放文件的具体数据内容)
场景四:通过配置自动生成RDB
新增配置项:save 10 1
(注意:更改完配置文件后一定要重启redis服务)
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> set k1 111
OK
127.0.0.1:6379> exit
在10s后重新观察dump.rdb
文件就可以发现重新执行了RDB生成过程
场景五:故意改坏RDB文件,观察启动现象
当我们尝试在dump.rdb
中间修改一些数据,使用kill -9 强制杀死redis服务,就会出现启动失败的现象,查看日志文件redis.log(在配置文件redis.conf可以配置)
我们也可以通过一些客户端工具,例如redis-check-rdb
检查RDB文件格式: