Redis进阶篇二——持久化

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1. 简介"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"持久化即将数据保存到可永久保存的存储设备中。我们知道 Redis 为了保证效率而把数据都缓存在内存中,但当我们重启系统或关闭系统后,缓存在内存中的数据都会消失,所以为了让有些数据能保留下,Redis 持久化存储就应运而生。Redis 提供了两种方式进行持久化,一种是"},{"type":"text","marks":[{"type":"strong"}],"text":"RDB 持久化"},{"type":"text","text":",另一种是 "},{"type":"text","marks":[{"type":"strong"}],"text":"AOF(append only file) 持久化"},{"type":"text","text":",下面我们逐一介绍。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2. RDB 持久化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RDB 是 Redis 默认的持久化机制,它的工作原理是把当前内存中的数据生成快照 (snapshot) 的方式写入磁盘中的二进制文件中,默认的文件名为 dump.rdb。恢复时将快照文件直接读到内存中。RDB 有两种触发方式,分别是自动触发和手动触发。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1 自动触发"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"自动触发使用 save 相关配置触发,比如 “save m n”,表示在 m 秒内数据库存在 n 次修改时,自动触发BGSAVE (BGSAVE 命令在手动触发时会介绍)。Redis 默认配置如下:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"save 900 1 # 在 900 秒内如果至少有 1 个 key 的值变化,则触发\nsave 300 10 # 在 300 秒内如果至少有 10 个 key 的值变化,则触发\nsave 60 10000 # 在 60 秒内如果至少有 10000 个 key 的值变化,则触发"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当实际操作满足配置的 save 形式时就会进行 RDB 持久化,将当前的数据快照保存。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.2 手动触发"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"手动触发进行 RDB 持久化涉及到两个 Redis 服务器命令:"}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"SAVE:执行一个同步保存操作,将当前 Redis 实例的所有数据快照以 RDB 文件的形式保存到磁盘中。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"BGSAVE:用于在后台异步保存当前数据库的数据到磁盘。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SAVE 命令由于是同步操作,因此会阻塞当前 Redis 服务器知道 RDB 持久化过程完成为止,对于内存比较大的实例会造成长时间阻塞,不建议在线上环境使用。BGSAVE 命令会执行 fork 操作创建一个子进程,由子进程完成 RDB 持久化过程,完成后自动结束,阻塞只发生在 fork 过程,一般时间很短。基本上 Redis 内部的 RDB 操作都是采用 BGSAVE 命令。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.3 RDB 优缺点"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RDB 的优点:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"RDB 文件是一个紧凑压缩的二进制文件,它保存了 Redis 在某个时间点的数据,比较适合进行备份和灾难恢复。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"生成 RDB 文件的过程是由父进程 fork 操作创建的一个子进程完成,父进程仍然可以接受其他命令请求,不用进行任何磁盘 IO 操作。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"由于 RDB 文件是二进制文件,因此在恢复数据集时速度更快。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RDB 的缺点:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"RDB 无法做到实时持久化/秒级持久化。因为 BGSAVE 命令需要执行 fork 操作创建子进程,如果频繁操作必然会占用大量内存,执行成本过高,反而使性能降低。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"RDB 文件使用特定二进制格式保存,Redis 版本演进过程中有多个格式的 RDB 版本,容易出现版本不兼容问题。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"RDB 是隔一段时间进行备份,如果 Redis 在备份时出现意外宕机,那么就会失去最后一次快照的数据。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3. AOF 持久化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"差别于通过保存数据库中的键值对的 RDB 持久化方式,AOF 持久化是通过保存 Redis 服务器所执行的写命令来记录数据库状态,重启时再重新执行 AOF 文件中的命令以完成数据恢复。AOF 的主要作用是解决了数据持久化的实时性,目前已经是 Redis 持久化的主流方式。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3.1 使用 AOF"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Redis 中 AOF 是默认关闭的,使用前要将配置参数 appendonly 改为 yes(5.3 中会涉及一些配置参数,配置文件是安装目录下的 redis.windows.conf,参数在 APPEND ONLY FILE 一栏)。AOF 文件的保存文件名通过参数 appendfilename 参数配置,默认是 appendonly.aof。AOF 持久化策略的选择由 appendfsync 参数进行选择,有下面几种:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"no:不执行 fsync,由操作系统保证数据同步到磁盘,速度快但不安全。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"always:每次写入都执行 fsync,虽然保证数据同步到磁盘,但是效率很低。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"everysec:默认配置,每秒执行一次 fsync,可能会丢失这 1 秒的数据,但兼顾了安全性和效率,最常用的选择。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3.2 AOF工作流程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AOF 的工作流程操作:命令写入 (append)、文件同步 (sync)、文件重写 (rewrite)、重启加载 (load)。首先,所有的写入命令都会被追加到 AOF 缓冲区中,然后 AOF 缓冲区根据对应的持久化策略对磁盘进行文件同步操作。当 AOF 文件的大小超过所设定的重写阈值时对 AOF 文件进行重写。当需要重写时,父进程会进行 fork 操作创建一个子进程,子进程带有父进程的数据副本,由子进程完成重写过程,在此期间父进程仍然可以处理其他命令。当我们重启 Redis 服务器时,可以加载 AOF 文件进行数据恢复,流程如下:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ec/ecead74dc219deb4892f04398f817e33.png","alt":"持久化","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"持久化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从上图我们也可以得知,"},{"type":"text","marks":[{"type":"strong"}],"text":"在同时开启了 RDB 和 AOF 的情况下,Redis 会优先 AOF 文件的加载"},{"type":"text","text":"。遇到异常时,可以使用修复命令 redis-check-aof--fix 进行修复。在上述流程中,我们有几点需要注意与知晓的:"}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"为什么需要 AOF 缓冲"},{"type":"text","text":":由于 Redis 本身是单线程工作的,如果每次都直接把写入命令追加到 AOF 文件中,那么此时的性能取决于磁盘的 IO 性能,会降低性能。先写入 AOF 缓冲区中,我们还可以选择缓冲区同步到磁盘的策略,进一步兼顾安全性和性能。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"为什么需要 AOF 重写"},{"type":"text","text":":由于 AOF 持久化是不断将写命令记录到 AOF 文件中,随着时间的推移,文件必然会越来越大,这样会增加恢复时的压力。除此之外,例如一个 key 表示粉丝数,增加粉丝的过程我们会使用大量自增命令,而实际上在恢复时我们只需要知道在这段时间内总共增加了多少即可。这个例子也正指明了重写机制的工作原理:"},{"type":"text","marks":[{"type":"strong"}],"text":"AOF 文件重写并非是对原文件进行整理,而是直接读取服务器中现有的键值对,然后用一条命令代替记录中改键值对的多条命令操作,生成一个新的文件替换原文件"},{"type":"text","text":"。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"AOF 重写触发机制"},{"type":"text","text":":AOF 重写也分自动触发和手动触发。"}]}]}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"自动触发涉及两个配置参数:一个是 "},{"type":"text","marks":[{"type":"strong"}],"text":"auto-aof-rewrite-percent"},{"type":"text","text":",默认值是 100;另一个是 "},{"type":"text","marks":[{"type":"strong"}],"text":"auto-aof-rewrite-min-size"},{"type":"text","text":",默认值是 64MB。按照默认配置,Redis 会记录上次重写时 AOF 文件大小,并当目前 AOF 文件是上一次重写后大小的一倍且文件大于 64MB 时自动触发。"}]}]}]},{"type":"paragraph","attrs":{"indent":2,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"auto-aof-rewrite-percent"},{"type":"text","text":":当目前 AOF 文件大小超过上一次重写的 AOF 文件大小的百分之几时进行重写。"},{"type":"text","marks":[{"type":"strong"}],"text":"auto-aof-rewrite-min-size"},{"type":"text","text":":设置允许重写的最小 AOF 文件大小,避免了文件很小却满足百分比要求的重写情况。"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"手动触发直接调用 BGREWRITEAOF 命令。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3.3 AOF 优缺点"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AOF 的优点:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"提供了多种同步频率,即使使用默认的同步频率每秒同步一次,Redis 最多也就失去 1 秒的数据。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"AOF 文件使用 Redis 命令追加的形式来构造,即使只能向 AOF 文件写入命令的片段,也很容易使用redis-check-aof 工具修正。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"AOF 文件的格式可读性较强,可以为使用者提供更灵活的处理方式。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AOF 的缺点:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"在拥有相同数据的情况下,AOF 文件通常会比 RDB 文件体积更大。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"当 Redis 负载较高的情况下,RDB 会比 AOD 具有更好的性能保证。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"RDB 使用快照的形式来持久化整个 Redis 数据,而 AOF 只是将每次执行的命令追加到 AOF 文件中,因此从理论上说,RDB 比 AOF 方式更健壮。官方文档也指出,AOF 的确也存在一些 BUG,这些 BUG 在RDB 没有存在。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"4. 如何选择"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"介绍了 Redis 持久化的两种方式,那么我们在实际中应该如何选择呢?对于数据库而言,数据是相当重要的,RDB 相比于 AOF 而言出现异常丢失数据可能会更严重,除此之外,选择 RDB 是更好的,定时生成快照是常用的数据库备份方式,并且 RDB 文件是二进制文件,在恢复数据集时速度更快,使用 RDB 方式也可以规避 AOF 方式中的一些隐藏 bug。但是在一般情况下,我们应该同时开启两种持久化方式,而不是单独使用某一种持久化机制。由于通常情况下 AOF 方式保存的数据更具实时性且更完整,因此相比 RDB 文件,Redis 重启时会优先使用 AOF 文件来恢复数据。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章