认识持久化
在MySQL的事务中,有4个比较核心的特性~
1、原子性
2、一致性
3、持久性 =》 (和我们下文要讲的持久化是一回事)
4、隔离性
我们知道把数据存储在硬盘上是持久的;而把数据存储在内存上是不持久的,重启进程/重启主机之后,数据是否存在是不确定的!!
而Redis是一个内存数据库,它是把数据存储在内存中的。而内存中的数据是不持久的,要想能够持久,就需要让redis把数据存储到硬盘上~
Redis相比于MySQL这样的关系型数据库,最明显的特点/优势 =》 效率高/快~~为了保证速度快,数据肯定还是得在内存中,但是为了持久,数据还得想办法存储在硬盘上......
那到底是存在硬盘还是存在内存中呢?
redis表示我全都要,把存在内存中的数据,在硬盘中也存一份,这样的两份数据,理论上是完全相同的,实际上可能存在一个小概率的差异,取决于咱们具体怎么进行持久化~
当插入一个新的数据的时候,就需要把这个数据,同时写入到内存和硬盘~说是两边都写,但是实际上具体怎么写硬盘还有不同的策略,主要是为了保证整体的效率足够高。当查询某个数据的时候,还是直接从内存中读取,硬盘的数据只是在redis重启的时候,用来恢复内存中的数据的~~
代价就是消耗了更多的空间,同一份数据,存储了两遍。但是,毕竟硬盘是比较便宜,这样的开销并不会带来太多的成本。
Redis实现持久化的策略
Redis实现持久化主要有两种策略:
1、RDB:定期备份,就像每个月我把资料整体得备份到这个备份盘中。
2、AOF :实时存储,只要我下载了一个资料,立即把这个资料往备份盘中进行拷贝。
RDB
RDB详解
刚才我们讲了,RDB是一种定期备份的方式,那么它到底是怎么做的呢?
RDB会定期地把我们Redis内存中的所有数据,生成一个"快照",都给写入硬盘中。相当于Redis给内存中当前存储的这些数据,赶紧拍个照片,生成一个文件存储在硬盘中。后续Redis一旦重启了,(内存数据就没了),就可以根据刚才的"快照",就能把内存中的数据给恢复回来~
实现"定期",又有两种方式:
1、手动触发
程序员通过redis客户端,执行特定命令,来触发快照生成。
save:执行save的时候,redis就会全力以赴地进行"快照生成",此时就会阻塞redis地其他客户端地命令~如果当前的数据量比较大,就会导致keys * 的后果,因此,一般不建议使用save。
bgsave:这个命令能够在不会影响Redis服务器处理其他客户端的请求和命令的情况下去生成快照。
那这里Redis是怎么做到的呢?是不是在背后搞了个多线程啥的?
并不是,此处Redis是使用多进程的方式来完成并发编程,完成bgsave的。
工作流程:
1、执行bgsave命令,Redis父进程判断当前进程是否存在其他正在执行的子进程,如RDB/AOF子进程,如果存在bgsave命令直接返回。
2、父进程执行fork创建子进程,fork过程中父进程会阻塞,通过info stats命令查看latest_fork_usec选项,可以获取最近一次fork操作的耗时,单位为微秒。
3、父进程fork完成后,bgsave命令返回"Background saving started"信息并不再阻塞父进程以继续响应其他命令
4、子进程创建RDB文件,根据父进程内存生成快照文件,完成后对原有文件进行原子替换,执行lastsave命令可以获取最后一次生成RDB的时间,对应info统计的rdb_last_save_time选项。
5、进程发送信号给父进程表示完成,父进程更新统计信息。
Redis这里采用"写时复制"的机制生成RDB,避免阻塞主进程,流程如下:
1、主线程受到RDB触发信号(如定时任务、手动执行save)
2、主线程调用fork创建一个子进程(此处的子进程与父进程共享内存页,不占用额外内存,除非父进程进行修改)
3、子进程负责遍历内存数据,生成RDB文件并写入磁盘。
4、主线程继续处理客户端请求:若期间有数据修改,Redis会为修改的内存页创建"副本",子进程仅操作原内存页(保证RDB快照是"fork时刻"的全量数据)
5、子进程完成RDB写入后,通知主线程并退出,旧RDB文件被新文件替代
2、自动触发
在Redis配置文件中,设置一下,让Redis每隔多长事件/每产生多少次修改就触发。
redis生成的rdb文件,是存放在redis的工作目录中的。也是在redis配置文件中进行设置的。
进入/etc/redis目录下,我们可以看到redis.conf(注意:这里如果是使用Xshell连接服务器,需要使用sudo -i切换为root身份连接服务器,才能进入当前目录!!!),这个是redis的配置文件!!!

我们使用vim命令进入查看:

这里的dir/var/lib/redis就是redis的工作目录。
进入当前的工作目录,可以看到当前目录下有一个dump.rgb文件,这个就是rgb机制生成的镜像文件,redis服务器默认是开启了rgb的。这是一个二进制的文件,把内存中的数据,以压缩(需要消耗一定的cpu资源,但是能够节省存储空间)的形式,保存到这个二进制文件中~~
我们vim进入这个rgb文件看看,注意:这里只能是看看,一旦把这个文件数据格式改坏了就麻烦了~~后续redis重启,就会尝试加载这个rgb文件(这个rgb文件,虽然咱们不主动去动它,但是也可能出现一些意外问题,一旦通过一些操作(比如一些网络传输)引起这个文件被破坏,此时redis服务器就无法启动),如果发现格式错误,就可能会加载数据失败~~
当执行rdb镜像操作的时候,此时就会把要生成的快照数据先保存到一个临时文件中,当这个快照生成完毕之后,再删除之前的rdb文件,把新生成的临时的rdb文件名字改成刚才的dump.rdb,也就是说,自始自终,rdb文件是只有一个的。
我们使用Xshell再开一个连接操纵Redis:
再进入rdb文件中看看当前的key和value是否被存储在rdb文件中:
可以看到当前数据并没有被存储进来,这是因为rdb文件中的数据,不是你这百年插入了数据就会立即更新的!
刚才我们也讲了rdb的触发时机可以分为手动(save,bgsave)和自动(配置文件中,进入设置)。
进入redis.conf中,可以看到当前字段:

我们刚才插入的几个键值对,没有手动触发rdb存储,也达不到自动触发的条件。
虽然此处的这些数值,都可以自由修改配置~~但是,此处修改上述数据的时候,要有一个基本原则:生成一次rgb快照,这个成本是比较高的成本,不能让这个操作执行的太频繁!!
正因为rdb生成的不能太频繁,这就导致,快照里的数据,和当前实时的数据情况可能存在偏差~~
从最后一行的配置中,我们得到:两次生成rdb之间的间隔,最少得是60s。这就可能会导致出现下图中的情况。
解决这个问题的办法:AOF(实时备份)。、
RDB效果演示
1、手动执行save&bgsave触发生成快照
此时我们再查看rdb文件:
由于咱们这里的数据比较少,执行bgsave瞬间就完成了,立即查看应该是有结果的,如果以后咱们接触到的数据多了,执行bgsave就可能需要消耗一定的时间,立即查看不一定就是生成完毕了。
我们再重启下redis服务器:
进入redis中查看key还是否存在:
通过上述操作,就可以看到,redis再重新启动的时候加载了rdb文件的内容,恢复了内存中之前的状态了。
2、插入新的key,不手动执行bgsave重新启动redis服务器
咦?这里我们刚才没有执行bgsave!!但是key4仍然再重启之后存在!!
这是因为,redis生成快照操作,不仅仅是手动执行命令才触发,也可以自动触发!!
1、通过刚才配置文件中的save执行M时间内,修改N次......
2、通过shutdown命令(redis中的一个命令)关闭redis,也会触发。(service redis-server restart)(正常关闭)。
3、redis进行主从复制的时候,主节点也会自动生成rgb快照,然后把rdb快照文件内容传输给从节点。
下面我们来看一种没法自动触发的情况:
先在redis中设置key:
使用kill-9强制杀掉redis进程:
这里注意:如果使用的是centos系统,这里杀掉redis进程之后就不会显示了,而不是像此处一样显示另一个端口号!!!
查看key5是否存在:
3、bgsave操作流程是创建子进程,子进程完成持久化操作。持久化会把数据写入到新的文件中,然后使用新的文件替换旧的文件。
这里第一步的速度太快了(数据少),难以观察到子进程。而新的文件替换旧的文件,是很容易观察到的(克隆)。
可以使用linux的stat命令,查看文件的inode编号来观察是否是一个文件~~
在Linux文件系统中,文件系统的典型组织方式(ext4)主要是把整个文件系统分成了三个大部分~~
1、超级块(放的是一些管理信息)
2、inode区(存放inode节点。每个文件都会分配一个inode数据结构,包含了文件的各种元数据)
3、block区,存放文件的数据结构

使用bgsave命令,完成持久化操作:
再次使用stat命令:

此处文件内容和大小虽然是一样的,但是因为执行了bgsave,文件已经不是同一个文件了(inode编号,就相当于文件的身份标识)。
这里注意,如果直接使用save命令,此时是不会触发子进程&文件替换的,如果save就直接在当前进程中,往刚才的同一文件中写入数据了~~
4、通过配置自动生成rdb快照
这里我们修改为当更新两条数据之后就会自动生成rdb快照:
这里注意:配置文件修改之后,一定要重新启动服务器,才能生效!!
这里执行flushall也会清空rdb文件:
rdb文件:

此时我们插入两条数据,等待1分钟后,再重新查看rdb文件:
此处,可以使用该选项,关闭自动生成快照

5、如果把rdb文件,故意改坏了,会咋样?
手动地把rdb文件内容改坏。
然后通过kill进程的(如果通过service redis-server restart的方式重新启动,redis会重新生成快照)方式重新启动redis服务器:
此时,重新进入redis,发现redis没有受到啥影响,还是能够正常启动,还是正常获取到key~~这里redis会咋样,取决于rdb文件坏了的地方在哪~~刚才改坏的地方正好是文件末尾,对前面的文件没啥影响。如果是中间位置坏了可就不一定了
改完之后可以可以看到redis服务器挂了!!!


如何处理呢?
当redis服务器挂了的时候,可以看看redis日志,了解一下发生了啥~~
这个是日志文件本体,后面几个是压缩包!!!
拖到最后可以看到日志显示:rdb恢复数据过程中出现问题了!!

redis也为我们提供了rdb文件的检查工具,可以先通过检查工具,检查一下rdb文件格式是否符合要求:
这里的检查工具和redis服务器在5.0版本是同一个可执行程序。可以在运行时加入不同的选项,从而使用其中不同的功能~~
运行的时候,加入rdb文件作为命令行参数,此时就是以检查工具的方式来运行,不会真的启动redis服务器:

那我们的redis服务器还是起不起来怎么办?
在我们当前的情况下,直接把刚才改废了的redis文件删除即可......而到了公司的话,数据重要的情况下,我们可以看看有没有备份的文件,给他重新写回去就行了。但是,大家还是不要去动这里的rdb文件!!!!
