redis持久化的两种方式:RDB和AOF
RDB 持久化是把当前进程数据⽣成快照保存到硬盘的过程,触发 RDB 持久化过程分为手动触发和⾃动触发。
手动触发分别对应 save 和 bgsave 命令:
• save 命令:阻塞当前 Redis 服务器,直到 RDB 过程完成为⽌,基本不采用。
• bgsave 命令:Redis 进程执行fork 操作创建⼦进程,RDB 持久化过程由子进程负责,完成后自动结束。阻塞只发生在 fork 阶段,⼀般时间很短。
Redis 内部的所有涉及 RDB 的操作都采⽤类似 bgsave 的方式。
除了手动触发之外,Redis 运行自动触发 RDB 持久化机制,这个触发机制才是在实战中有价值的。

1,RDB:RDB是一个紧凑的二进制文件,代表redis在某一时刻的数据快照。
2,Redis加载RDB比AOF的方式更快:RDB这里使用的是二进制的方式来组织数据的,直接把数据加载到内存中,按照字节的格式去出来,放到结构体/对象中即可。而AOF是使用文本的方式来组织数据的,这会涉及到一些字符串分割操作。
3,RDB文件使用特点的二进制格式保存,Redis版本演进过程中,有多个RDB版本没兼容性可能有风险。
不能实时的持久化保存数据。在两次生成快照之间,实时的数据可能会随着重启而丢失。
但是AOF写文件的本质仍然是向内存中写,如果redis服务器挂了,那么内存中的数据仍然是会丢失的。
所以redis给出了一些选项,来决定缓冲区的刷新策略。
everysec表示每秒执行刷新的操作;no,频率最低,将什么时候刷新交给操作系统,比如当服务退出了,执行刷新操作,当缓冲区满了,执行刷新操作。
比如现在有以下几个操作: 1,set key 111,set key 222,set key 333,最后整合成一个命令set key 333,也就是前面的两个操作不用保存,只保留最后一个即可。 2,同理还有对列表的操作:lpush key 111,lpush key 222,lpush key 333,这几个操作最后也可以整合成一个命令:lpush key 111 222 333 通过这样的方式,可以减少aof文件的大小。
bgrewriteaof命令。
当触发AOF文件重写机制时:

在上述重写流程中,父进程fork之后,子进程就开始写新的aof文件了,并随着时间的推移,子进程很快就写完了新的文件,要让新的aof文件代替旧的aof文件。在这个过程中,父进程仍然在继续写这个即将消亡的旧的aof文件,是否还有意义?
注意,这里不能不写,考虑到极端情况,如果子进程在重写的过程中,服务器挂了,子进程内存中的数据就会丢失,此时 新的aof文件中的内容还不完整。所以,如果父进程不坚持写旧的aof文件,重启之后就无法保证数据的完整性了。
如果,在执行bgrewriteaof的时候,当前redis已经正在进行aof重写了,此时不会再执行重写了,会直接返回。
如果,在执行bgrewriteaof的时候,发现redis正在生成rdb文件的快照,此时,aof重写操作就会等待,等待rdb快照生成完成之后,再执行aof重写。
注意:AOF本来是按照文本的方式来写入文件的,但是以文本的方式写文件,后续加载的成本是比较高的。 所以redis就引入了"混合持久化"的方式,结合了rdb和aof的特点。 按照aof的格式 ,每次的请求/操作,都记录写入文件(文本的形式)。 在触发aof重写之后,就会把当前内存的状态按照rdb的二进制形式写入 aof文件中。 后续再进行操作,仍然是按照aof文本的方式追加到文件后面。

配置文件中的这个选项,就表示开启"混合持久化"的方式。
redis的事务,类似于MySQL中的事务,但是相比于MySQL,redis的事务简单了不少。
关于MySQL中的事务,简单回顾下: 原子性:把多个操作打包成一个整体。 一致性:事务执行前后,数据都必须是正确的。 持久性:事务中做出的修改都会存硬盘,保证服务器在重启之后 ,数据仍然存在。 隔离性:事务的并发执行,会涉及到的一系列问题(比如脏读,幻读,不可重复读等)。
redis事务:
redis的事务,主要的意义,就是"打包",避免其他客户端的命令,插队插到中间。
redis中实现事务,是引入了队列(每个客户端都有一个)。
开启事务的时候,此时客户端输入的命令,就会发给服务器并且进入这个队列中(而不是立即执行)。
当遇到了"执行事务"的命令,此时就会把队列中的这些命令按照顺序依次推送给服务器去执行。
开启事务:MULTI
执行事务:EXEC
放弃当前事务:DISCARD
在redis事务中,还提供了WATCH和UNWATCH两个命令。
首先引入下面的场景:

set key 222,客户端2后发送了set key 333,按理来说应该是后发送的生效,也就是key的最终值是 333。
set key 222。因此这个操作变成了更晚的操作,所以key的最终值是222,而这也符合事务的特性。
WATCH来监视这个key,看看这个key在事务的MULTI和EXEC之间,set key 之后,是否有外部 其他的客户端修改这个key。如果有,会给出提示。
UNWATCH命令,这个就是解除对某个key的监视。
效果演示:
在执行exec后,在执行上述事务中的命令时,发现key在外部有修改,那么在执行set key 222时,就没有真正执行,返回接轨是nil,不是ok。

WATCH是如何知道其他客户端修改了这个key呢?也就是WATCH是如何实现的。
WATCH的实现,类似于一个"乐观锁"。
所谓乐观锁和悲观锁,不是指某个具体的锁,而是指某一类锁的特性。 乐观锁:加锁之前,就有一个心理预期,预期接下来的锁冲突(锁竞争)概率比较低。 悲观锁:加锁之前 ,也有一个心理预期,预期接下来的锁冲突(锁竞争)概率比较高。 而锁冲突概率高,和所冲突概率低,接下来要做的工作是不一样的。
redis的WATCH就使用相当于"版本号"这样的机制,来实现了"乐观锁"。依旧是上面的例子,不过此时加上了WATCH来监控key 。
