乐趣区

关于java:漫画理解redis持久化和主从复制

内存和磁盘

为了不便了解,假如某公司暂存文件材料的 柜子 ,有多个格子,柜子还有一个专门的管理员 小张 ,他的工作就是按公司员工的命令在某个格子(key) 存材料 (value),取材料(value)。
这个柜子在办公室内为了不便疾速存取,也不关门也不锁,总之就是不便。

公司除了这个 柜子 能够存储材料,还有一个专门的 材料仓库 也能够存储材料,而且仓库有专人守护,保障材料个别状况必定丢不了。

这个 小张 柜子 就是是 redis,其中 柜子 就是内存,存取很快,但一重启数据就清空。
这个 材料仓库 就是磁盘,存取比较慢,但文件不会失落。
公司员工就相当于客户端,能够给 小张 发命令存取材料。
柜子只有一个管理员 小张 对应 redis 的单线程。
本文例子里的所有人物就相当于执行程序的cpu,脑子忘性为 0,只能靠纸笔记录。

RDB

某天有个领导说这个 柜子 用的很不便,但心愿材料不会失落。
于是有人提出计划,计划名叫 RDB,思路比较简单,每隔一段时间,把以后材料存储的地位和内容全都记录到一个快照文件里,并把快照文件放到 存储仓库 ,快照文件的格局相似如下

某天保洁来了把 柜子 清空了,材料都失落了 (相当于 redis 重启),按 仓库 中的快照文件还原材料即可。
具体隔多久存一次呐? 能够有多种准则,比方 “60 秒内有至多有 100 个格子被改变 ” 就快照一次。

redis:
RDB 快照(snapshot)是 redis 默认的长久化计划,只有设置好保留的工夫策略 (能够多个) 就默认开启 rdb,除此之外还能够设置快照文件名和保留的门路等:

save 3600 1 #3600 秒 1 次 key 变更
save 300 100 #300 秒 100 次 key 变更
save 60 10000 #60 秒 10000 次 key 变更
dbfilename dump.rdb ## 存储文件名
dir ./  ## 存储门路

存储好之后,下次重启会间接按 rdb 文件复原数据 (实现了长久化)。

内容如下(以二级制的模式压缩保留)

刚开始 小张 本人去记录快照并去材料仓库存储,然而这个过程很慢,导致这期间其他人想存材料只能等。
为了解决这个问题,公司新给他配加一个人 小李 ,这样 小张 还是干之前的活,小李 负责在规定条件下记录并存储快照文件,在 小李 工作的期间 (记录并去材料仓库存储),如果有新的存储变动, 小张 还会告诉 小李

redis:
下面 小张 相当于主线程,由主线程去快照并长久化的形式叫 save,该过程是同步的阻塞客户端申请, 小李 退出相当于新 fork 一个子线程专门解决 rdb,这种叫 bgsave(background save),该过程是异步的不阻断客户申请,而且过程中有新的变动还会同步(Copy-On-Write),redis 默认应用bgsave
总结一下 bgsave 必定在性能上优于save,因为不阻塞申请,然而新开一个过程必定要消耗更多资源。

RDB 计划有一个致命的问题,小李 须要触发肯定条件才回去做快照工作,比方十分钟做一次,但在这十分钟内保洁来了把柜子清掉了 (相当于 redis 重启),这非常种内新增的材料 小李 还没来得及记录就丢了,而且再也找不回来了。

redis:
与其对应,rdb 存储最大的问题就是容易造成数据失落,比方新数据还没来得及做快照,redis 就重启了,那么这些新增的数据就丢了。

AOF

为了解决文件失落的问题,公司提出一个新计划,名曰 AOF(append-only-file),就是让柜子管理员 小张 柜子 里放一张纸,每次收到员工的存储申请,解决完就把申请内容用记录到纸上,并在 失当的机会 把记录转到 仓库 (落盘)。

那么什么是 失当的机会 呐?公司提出三种计划:
PlanA. 每次有存储申请就记录一次。
PlanB. 每隔一秒记录一次。
PlanC. 公司下发的命令就记录一次。
小张 能够依据公司要求切换计划。
有了这份 AOF 文件,当保洁清理掉所有柜子文件后,只有依照 AOF 文件的记录一条一条执行就会复原柜子原来的样子。
再看一眼 AOF 的记录

第一条和第四五条别离是 01 格存了 A,B,C,这样显得文件有太多没用的指令,因为最终目标是为了复原数据,所以只有存第五条:“在 01 格存储了 C”即可,前两条数据多余,而且会造成文件大,读写工夫慢,复原数据也慢。
这时候本分案另一个闲置人员 小李 退场了,他负责整顿 AOF 文件,也就是重写,后果如下:

redis:
以上过程就是 redis 的 AOF,开启 AOF 长久化形式,须要批改配置

appendonly yes # 开启 AOF
appendfsync always # 每次有新命令追加到 AOF 文件时就执行一次 fsync,十分慢,也十分平安。appendfsync everysec# 每秒 fsync 一次,足够快,并且在故障时只会失落 1 秒钟的数据。appendfsync no # 从不 fsync,将数据交给操作系统来解决。更快,也更不平安的抉择。

其中 appendfsync 三选一,别离对应实时同步,每秒同步,操作系统同步三种存储形式,安全性从高到低,效率从低到高,默认是 everysec。
开启之后执行一条指令

set pq 217

查看 AOF 文件

外面的内容如下

其实就是记录每条命令。
上例中 小李 的工作就是 AOF 重写,目标就是删除多余指令,redis 是通过新 fork 一个线程来做重写的,能够通过配置 auto-aof-rewrite- 参数来调整重写的策略。
比照一下: RDB 形式不平安容易失落数据,AOF 比拟平安,然而文件大、一条条命令执行来复原数据工夫长(即便有重写也达不到 rdb 的成果)

混合长久化

RDB、AOF 两种计划各有优缺点,于是很快就有人发现了,为什么不两种形式混合应用呐。
计划如下:
小李 再做重写的时候看一眼 AOF 文件,比方以后行是 10 行,记住,而后新做一个文件,存储 RDB 格局的快照数据,在这期间如果有新的指令来了 小张 会存入原 AOF 文件,小李 做完快照看一眼原 AOF 文件,比方到了 12 行,代表这期间新增了两行,那么把这两行剪切下来粘到新文件中,最初用新的文件替换并笼罩原 AOF 文件(最终的 AOF 文件两种格局混搭),复原时候先复原 RDB 局部,再执行大量的 AOF 命令,很快就复原了数据而且还保障了平安。

redis:
应用 redis 很少应用 RDB 形式来进行长久化,因为会失落大量数据。通常应用 AOF 日志重放,然而重放 AOF 日志性能绝对 RDB 来说要十分慢,所以应用混合长久化形式最好,开启混合长久化须要配置(默认是开启的)

aof-use-rdb-preamble yes # 开启混合长久化

从下面的例子能够看出混合长久化是基于 AOF 的重写 (只是应用 RDB 技术),所以 AOF 必须开启(并且配置重写策略),重写后的 AOF 个别如下

开启之后的益处下面也说了,平安的同时又重启快。

主从复制

公司一直壮大,用柜子的人也越来越多,而且大部分是为了查看,柜子倒是够用,但 小张 及其疲乏,为了加重 小张 的压力,公司决定多上几个柜子,每个柜子都配置不同的管理员,为了资源对立,每个柜子寄存的文件截然不同,大家须要存材料对立找 小张 ,须要读材料去找其它柜子的管理员即可,这样大大加重了 小张 的压力。
按以上计划,小张 是主,其它管理员是从,这就是一个典型的主从构造。

那么问题来了,要保障每个柜子寄存的文件截然不同,怎么实现主从复制?
留神:下面介绍了长久化的计划,然而在主从复制时不肯定开启了长久化,资料库 可能不存在 AOF 和 RDB 文件,所以不能依附长久化来解决
计划如下:
比方上面的某个柜子管理员叫 小美
1. 小美 通知 小张 我要同步数据
2. 小张 告诉上司 小李 做一个 RDB 快照文件 (bgsave),与此同时 小张 持续承受申请,并记录申请的内容 (相似 AOF,但不存入材料仓库)
3. 小李 做完 RDB 快照也不必存库,间接发给 小美
4. 小美 清空柜子,按 RDB 快照从新安排柜子
5. 小张 把这段时间新增的申请指令发给 小美
6. 小美 按新增命令批改柜子数据,此时两个柜子数据统一
7. 之后 小张 承受新命令时本人存完,再通知 小美 存一下,此时两个柜子实时同步

redis:
redis 实现主从复制就是依照下面例子的思路来的:
master 收到同步命令后,会通过 bgsave 异步生成最新的 rdb 快照文件,这期间期间,master 会持续接管客户端的申请,它会把这些可能批改数据集的申请缓存在内存中。rdb 实现当前,master 会把这份 rdb 发送给 slave,slave 会把接管到的数据加载到内存中。而后,master 再将之前缓存在内存中的指令集发送给 slave。

主从复制(断点续传)

还是下面的例子,小美 的柜子通过主从复制始终放弃和 小张 统一,然而问题呈现了:
小美 有时候要去上厕所 (从节点掉了),那么这段时间 小张 柜子里的新增的数据就没方法同步到 小美 的柜子里了。
为了解决这个问题,小张 每次接管到新的写命令都会记录在一个小本上存到柜子里 (内存),而且只保留最近的几条记录,比方 10 条(不能存太多,一是查找吃力,二是占用柜子空间),其它都删掉,并且每条记录都有序号 01,02,03…,再同步命令的时候, 小美 也记录下本人以后的序号 (偏移量),这样 小美 上厕所回来后只有通知 小张 序号,小张 把序号前面的命令发给 小美 ,就实现了断点续传。

如果 小美 身材不难受,去厕所去了很久,回来之后把序号报给 小张 小张 发现这个序号早就不在本人的小本本上了,这时候就依照之前的初始复制计划整体复制一遍即可。

redis:
断点续传根本就是如上例这么个形式,master 会在其内存中开拓缓存队列,缓存最近一段时间的指令,master 和它所有的 slave 都保护了复制的数据下标 offset,因而,当网络连接断开后,slave 会申请 master 持续进行未实现的复制,从所记录的数据下标开始。如果从节点数据下标 offset 太旧,曾经不在 master 的缓存队列里了,那么将会进行一次全量数据的复制。

总结

其实长久化也好,主从复制也好,redis 的诉求都一样,就是 不阻塞用户申请 ,在 jvm 垃圾回收也有相似的诉求,即尽量避免 stw(stop-the-world)。
而解决的计划简略概括:增量更新,也就是对于同一数据后盾线程一边记,用户申请一边改,在过程中记录改了什么,最初把后果按改变内容调整一下就实现了最终的数据备份。
over~ 新人求赞!

退出移动版