持久化通俗地理解就是把内存中的数据,存到硬盘中。
Redis是一种内存数据库,它的数据是存放在内存中的,也就是说如果断电,数据就会丢失(RAM断电失忆)。
Redis中的数据就是键值对。
Redis持久化,把数据存到硬盘上,可以防止服务器出现故障造成数据丢失,这时只需要从硬盘中存放的数据重新加载到内存,那么Redis就可以恢复状态。
如何恢复状态?
这里恢复状态就有两种方案了:
- 把原来的数据存放到硬盘上,恢复时直接重新读入内存。
- 把操作存放到硬盘上,恢复时把这些操作重新执行一遍,同样可以恢复到之前的状态。
如何理解上面两种方案呢?
看下面python代码:
a = 1
b = 2
a = 3
上边代码的执行结果是在名字空间中存放了一对约束:{a:3, b:2}
现在我们要保存程序的执行结果:
就有两种方式:
- 直接把结果a:3, b:2存放起来,恢复时,直接把这对约束放到名字空间。
- 我们存放a=1,b=2,a=3, 这几条语句,恢复时,重新把这三条语句执行一遍,同样也可以恢复到{a:3, b:2}这个状态。
这就是Redis的两种持久化方式:RDB, AOF
RDB持久化:
rdb.c [代码已删减]
核心代码在上面列出,其实很明显,就是把所有的键值对取出,然后保存。对应我们讨论的第一种持久化方式,直接保存要恢复的状态(最后的结果)。
AOF持久化:
aof持久化比较复杂,与整个服务器进程都有很大的关系,不做过多分析。
只做一些简要说明:
- 并不是每执行一条命令都把这条命令(操作)直接保存到硬盘,而是先放到缓冲区中。
- Redis会对这些命令进行优化:比如上面我们列举的三条赋值语句a=1, b=2, a=3, 如果直接存放三条语句,那么第一条语句是没有用的,只需要存放b=2, a=3, 即可恢复状态。所以redis的优化就是先读出当前键值对的值,然后组织出语句,再放到缓冲区中。(读出当前名字空间中的键值对为{a:3, b:2}, 那么就组织出语句a=3, b=2, 存放这两条语句)
事件溯源:
AOF就相当于是事件溯源,只存储记录,不存放状态,需要具体状态时,只要从头计算所有记录(执行所有操作)即可。所需要的存储空间更大。
事件溯源由于没有更新和删除,只有增加(新增一条语句)和查询(执行所有语句),不维护可变变量,也就没有并发的问题。所有的并发问题(竞争,死锁,并发更新),都是由可变变量导致的:如果变量不能被更改,就不会有竞争和并发更新问题,如果锁状态不能更改,就不会有死锁问题。
如果存储状态,这个状态就是可变变量。(Redis中的键值对可能被随时修改)。
用Redis的RDB与AOF(事件溯源)相比,可能不太准确。因为RDB是把所有数据库的所有键值对全部存放起来的。不是单个修改,也不是事务。
用事务中大家最熟悉的存取款来解释事件溯源的优点:
如果我们存放的是状态,也就是账户的余额,那么每次存取款时,就要同时修改余额记录(这是个可变变量,要保证并发安全)。
如果我们存放的是所有的操作,不存放状态,需要具体状态时,只要从头计算所有记录(执行所有操作)即可。由于没有更新和删除,只有增加(新增一条语句)和查询(执行所有语句),不维护可变变量,也就没有并发的问题。
本文主要是讨论两种持久化的思路,对于细节没有做过多讨论。
后续应该会更新。
参考:《Redis设计与实现》
《Clean Architecture》