暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Redis相关内容分享--6

原创 阿布 2022-09-14
466

持久化

提供了多种不同级别的持久化方式:

  • RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。

  • AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。Redis 还可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下, 当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。你甚至可以关闭持久化功能,让数据只在服务器运行时存在。

    t1 --> 3. t1—>4. t1–>5

了解 RDB 持久化和 AOF 持久化之间的异同是非常重要的, 以下几个小节将详细地介绍这这两种持久化功能, 并对它们的相同和不同之处进行说明

RDB 的优点

  • RDB是一种表示某个即时点的Redis数据的紧凑文件。RDB文件适合用于备份。例如,你可能想要每小时归档最近24小时的RDB文件,每天保存近30天的RDB快照。这允许你很容易的恢复不同版本的数据集以容灾。
  • RDB非常适合于灾难恢复,作为一个紧凑的单一文件,可以被传输到远程的数据中心,或者是Amazon S3(可能得加密)。
  • RDB最大化了Redis的性能,因为Redis父进程持久化时唯一需要做的是启动(fork)一个子进程,由子进程完成所有剩余工作。父进程实例不需要执行像磁盘IO这样的操作。
  • RDB在重启保存了大数据集的实例时比AOF要快。

RDB 的缺点

  • 当你需要在Redis停止工作(例如停电)时最小化数据丢失,RDB可能不太好。你可以配置不同的保存点(save point)来保存RDB文件(例如,至少5分钟和对数据集100次写之后,但是你可以有多个保存点)。然而,你通常每隔5分钟或更久创建一个RDB快照,所以一旦Redis因为任何原因没有正确关闭而停止工作,你就得做好最近几分钟数据丢失的准备了。

    save 300 100

    save 90000 100

    save 10 10

  • RDB需要经常调用fork()子进程来持久化到磁盘。如果数据集很大的话,fork()比较耗时,结果就是,当数据集非常大并且CPU性能不够强大的话,Redis会停止服务客户端几毫秒甚至一秒。AOF也需要fork(),但是你可以调整多久频率重写日志而不会有损(trade-off)持久性(durability)。

AOF 的优点

  • 使用AOF Redis会更具有可持久性(durable):你可以有很多不同的fsync策略:没有fsync,每秒fsync,每次请求时fsync。使用默认的每秒fsync策略,写性能也仍然很不错(fsync是由后台线程完成的,主线程继续努力地执行写请求),即便你也就仅仅只损失一秒钟的写数据。
  • AOF日志是一个追加文件,所以不需要定位,在断电时也没有损坏问题。即使由于某种原因文件末尾是一个写到一半的命令(磁盘满或者其他原因),redis-check-aof工具也可以很轻易的修复。
  • 当AOF文件变得很大时,Redis会自动在后台进行重写。重写是绝对安全的,因为Redis继续往旧的文件中追加,使用创建当前数据集所需的最小操作集合来创建一个全新的文件,一旦第二个文件创建完毕,Redis就会切换这两个文件,并开始往新文件追加。
  • AOF文件里面包含一个接一个的操作,以易于理解和解析的格式存储。你也可以轻易的导出一个AOF文件。例如,即使你不小心错误地使用FLUSHALL命令清空一切,如果此时并没有执行重写,你仍然可以保存你的数据集,你只要停止服务器,删除最后一条命令,然后重启Redis就可以。

AOF 的缺点

  • 对同样的数据集,AOF文件通常要大于等价的RDB文件。

  • AOF可能比RDB慢,这取决于准确的fsync策略。通常fsync设置为每秒一次的话性能仍然很高,如果关闭fsync,即使在很高的负载下也和RDB一样的快。不过,即使在很大的写负载情况下,RDB还是能提供能好的最大延迟保证。

  • 在过去,我们经历了一些针对特殊命令(例如,像BRPOPLPUSH这样的阻塞命令)的罕见bug,导致在数据加载时无法恢复到保存时的样子。这些bug很罕见,我们也在测试套件中进行了测试,自动随机创造复杂的数据集,然后加载它们以检查一切是否正常,但是,这类bug几乎不可能出现在RDB持久化中。为了说得更清楚一点:Redis AOF是通过递增地更新一个已经存在的状态,像MySQL或者MongoDB一样,而RDB快照是一次又一次地从头开始创造一切,概念上更健壮。但是,1)要注意Redis每次重写AOF时都是以当前数据集中的真实数据从头开始,相对于一直追加的AOF文件(或者一次重写读取老的AOF文件而不是读内存中的数据)对bug的免疫力更强。2)我们还没有收到一份用户在真实世界中检测到崩溃的报告。

RDB 和 AOF ,我应该用哪一个?

一般来说,如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。

如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。

有很多用户单独使用AOF,但是我们并不鼓励这样,因为时常进行RDB快照非常方便于数据库备份,启动速度也较之快,还避免了AOF引擎的bug。

注意:基于这些原因,将来我们可能会统一AOF和RDB为一种单一的持久化模型(长远计划)。

下面的部分将介绍两种持久化模型等多的细节。

RDB 快照

默认情况下,Redis保存数据集快照到磁盘,名为dump.rdb的二进制文件。你可以设置让Redis在N秒内至少有M次数据集改动时保存数据集,或者你也可以手动调用SAVE或者BGSAVE命令。

例如,这个配置会让Redis在每个60秒内至少有1000次键改动时自动转储数据集到磁盘:

save 60 1000

save 900 100

save 10 10

这种策略被称为快照。

快照的运作方式

当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:

  • Redis 调用 fork() ,同时拥有父进程和子进程。
  • 子进程将数据集写入到一个临时 RDB 文件中。
  • 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。

这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益。

只追加文件 AOF

快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。尽管对于某些程序来说, 数据的耐久性并不是最重要的考虑因素, 但是对于那些追求完全耐久能力(full durability)的程序来说, 快照功能就不太适用了。

从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化。

你可以通过修改配置文件来打开 AOF 功能:

appendonly yes

从现在开始, 每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到 AOF 文件的末尾。 当 Redis 重新启时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。

日志重写

你可以猜得到,写操作不断执行的时候AOF文件会越来越大。例如,如果你增加一个计数器100次,你的数据集里只会有一个键存储这最终值,但是却有100条记录在AOF中。其中99条记录在重建当前状态时是不需要的。

于是Redis支持一个有趣的特性:在后台重建AOF而不影响服务客户端。每当你发送BGREWRITEAOF时,Redis将会写入一个新的AOF文件,包含重建当前内存中数据集所需的最短命令序列。如果你使用的是Redis 2.2的AOF,你需要不时的运行BGREWRITEAOF命令。Redis 2.4可以自动触发日志重写(查看Redis 2.4中的示例配置文件以获得更多信息)。

AOF持久性如何?

你可以配置 Redis 多久才将数据 fsync 到磁盘一次。有三个选项:

  • 每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全。
  • 每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。
  • 从不 fsync :将数据交给操作系统来处理。更快,也更不安全的选择。

推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。 总是 fsync 的策略在实际使用中非常慢, 即使在 Redis 2.0 对相关的程序进行了改进之后仍是如此 ------ 频繁调用 fsync 注定了这种策略不可能快得起来。

AOF有3种方式将操作命令存入AOF文件

1. appendfsync no 不保存

只执行WHRITE操作,SAVE操作会被略过,只有在Redis被关闭AOF功能被关闭系统的写缓存被刷新(如缓存已被写满)这三种情况,SAVE操作会被执行,但是这三种情况都会引起Redis主进程阻塞

2. appendfsync everysec 每秒钟保存一次

这种模式中,SAVE原则上每隔一秒钟就会执行一次,具体的执行周期和文件写入、保存时,Redis所处的状态有关,此模式下SAVE操作由后台子线程调用,不会引起服务器主进程的阻塞

3. appendfsync always 每执行一个命令保存一次
在这种模式下,每执行一个命令,WRITESAVE都会被执行,且SAVE操作会阻塞主进程

模式 WRITE阻塞 SAVE阻塞 停机时丢失的数据量
appendfsync no 不阻塞 阻塞 操作系统最后一次对 AOF 文件触发 SAVE 操作之后的数据
appendfsync everysec 阻塞 不阻塞 一般情况下不超过 2 秒钟的数据
appendfsync always 阻塞 阻塞 最多只丢失一个命令的数据

*设置好AOF写入的模式之后,只要达到写入条件(比如一秒钟、执行一个命令),就会自动在指定路径下生成AOF文件,并往里面记录操作命令*

no-appendfsync-on-rewrite参数

bgrewriteaof机制,在一个子进程中进行aof的重写,从而不阻塞主进程对其余命令的处理,同时解决了aof文件过大问题。

现在问题出现了,同时在执行bgrewriteaof操作和主进程写aof文件的操作,两者都会操作磁盘,而bgrewriteaof往往会涉及大量磁盘操作,这样就会造成主进程在写aof文件的时候出现阻塞的情形,现在no-appendfsync-on-rewrite参数出场了。如果该参数设置为no,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题。如果设置为yes呢?这就相当于将appendfsync设置为no,这说明并没有执行磁盘操作,只是写入了缓冲区,因此这样并不会造成阻塞(因为没有竞争磁盘),但是如果这个时候redis挂掉,就会丢失数据。

如果 AOF 文件出错了,怎么办?

服务器可能在程序正在对 AOF 文件进行写入时崩溃(这个不应该破坏数据的一致性), Redis不会装载已破坏的AOF文件。当发生这种情况时, 可以用以下方法来修复出错的 AOF 文件:

  • 为现有的 AOF 文件创建一个备份。
  • 使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复。
  • $ redis-check-aof --fix
  • (可选)使用 diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,查看两个文件之间的不同之处。
  • 重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。

如何工作

日志重写采用了和快照一样的写时复制机制。下面是过程:

  • Redis调用fork()。于是我们有了父子两个进程。
  • 子进程开始向一个临时文件中写AOF。
  • 父进程在一个内存缓冲区中积累新的变更(同时将新的变更写入旧的AOF文件,所以即使重写失败我们也安全)。
  • 当子进程完成重写文件,父进程收到一个信号,追加内存缓冲区到子进程创建的文件末尾。
  • 搞定!现在Redis原子性地重命名旧文件为新的,然后开始追加新数据到新文件。

如何由RDB持久化转换到AOF持久化?

Redis 2.0 和 Redis 2.2 处理流程不一样,可以很简单猜测到 Redis 2.2 处理流程更简单,并且不需要重启。

Redis >=2.2 时

  • 创建最近的RDB文件的备份。
  • 将备份保存在安全的位置。
  • 发起如下命令。
  • $redis-cli config set appendonly yes。
  • $redis-cli config set save “”。
  • 确认数据库包含相同的keys。
  • 确认write操作被正确追加到了AOF文件。

第一个CONFIG命令开启AOF。Redis会阻塞以生成初始转储文件,然后打开文件准备写,开始追加写操作。

第二个CONFIG命令用于关闭快照持久化。这一步是可选的,如果你想同时开启这两种持久化方法。

**重要:**记得编辑你的redis.conf文件来开启AOF,否则当你重启服务器时,你的配置修改将会丢失,服务器又会使用旧的配置。

Redis2.0时

  • 创建最近的RDB文件的备份;
  • 将备份存放在安全的位置;
  • 停止数据库上的所有写操作;
  • 发起 redis-cli bgrewriteaof命令创建AOF文件;
  • 当AOF文件生成后停止Redis Server;
  • 编辑redis.conf开启AOF持久化;
  • 重启Redis Server;
  • 确认数据库包含相同的keys;
  • 确认write操作被正确追加到了AOF文件。

AOF与RDB之间的相互作用

Redis2.4以上的版本会确保在RDB快照创建时不触发AOF重写或者在AOF重写时不允许BGSAVE操作,以避免Redis后台进程同时做繁重的磁盘I/O操作。

当创建RDB快照时对于用户使用BGREWRITEAOF明确发起的日志重写操作server会立刻回应一个ok状态码告知用户操作将会被执行,当且仅当快照创建完成后重写操作开始被执行。

在同时使用了AOF和RDB方式的情况下,Redis重启后会优先使用AOF文件来重构原始数据集。

备份Redis 数据

开始这一部分之前,请务必牢记:一定要备份你的数据库。磁盘损坏,云中实例丢失,等等:没有备份意味着丢失数据的巨大风险。

Redis对数据备份非常友好,因为你可以在数据库运行时拷贝RDB文件:RDB文件一旦生成就不会被修改,文件生成到一个临时文件中,当新的快照完成后,将原子性地使用rename(2)修改文件名为目标文件。

这意味着,在服务器运行时拷贝RDB文件是完全安全的。以下是我们的建议:

  • 创建一个定时任务(cron job),每隔一个小时创建一个RDB快照到一个目录,每天的快照放在不同目录。
  • 每次定时脚本运行时,务必使用find命令来删除旧的快照:例如,你可以保存最近48小时内的每小时快照,一到两个月的内的每天快照。注意命名快照时加上日期时间信息。
  • 至少每天一次将你的RDB快照传输到你的数据中心之外,或者至少传输到运行你的Redis实例的物理机之外。

灾难恢复

在Redis中灾难恢复和数据备份基本上是同样的过程,并且灾难恢复会将这些备份传输到外部的多个数据中心。这样即使一些灾难性的事件影响到运行Redis和生成快照的主数据中心,数据也是安全的。

由于许多Redis用户都处于启动阶段,没有太多预算,我们会介绍一些最有意思的灾难恢复技术,而不用太多的花销。

  • Amazon S3和一些类似的服务是帮助你灾难恢复系统的一个好办法。很简单,只需要将你的每日或每小时的RDB快照以加密的方式传输到S3。你可以使用 gpg -c 来加密你的数据(以对称加密模式)。确保将你的密码保存在不同的安全地方(例如给一份到你的组织中的最重要的人)。推荐使用多个存储服务以提升数据安全。
  • 使用SCP(SSH的组成部分)来传输你的快照到远程服务器。这是一种相当简单和安全的方式:在远离你的位置获得一个小的VPS,安装ssh,生成一个无口令的ssh客户端key,并将其添加到你的VPS上的authorized_keys文件中(译者注:这是SSH互信,在Linux系统中可以使用ssh-keygen命令生成公私钥)。你就可以自动的传输备份文件了,无需输入密码。为了达到更好的效果,最好是至少从不同的提供商那搞两个VPS。

要知道这种系统如果没有正确的处理会很容易失败。至少一定要确保传输完成后验证文件的大小(要匹配你拷贝的文件),如果你使用VPS的话,可以使用 SHA1 数字签名。

你还需要一个独立的告警系统,在某些原因导致传输备份过程失败时告警。

持久化备份恢复实操

1,首先在6379端口进行save操作(并且开启了appendonly),会生成dump.rdb文件

root@VM-2-10-ubuntu:/usr/local/redis/db/db1# redis-cli -p 6379 127.0.0.1:6379> save OK 127.0.0.1:6379> exit root@VM-2-10-ubuntu:/usr/local/redis/db/db1# ll total 16 drwxr-xr-x 2 root root 4096 Aug 15 08:59 ./ drwxr-xr-x 8 root root 4096 Aug 12 16:31 ../ -rw-r--r-- 1 root root 270 Aug 12 18:47 appendonly.aof -rw-r--r-- 1 root root 287 Aug 15 08:59 dump.rdb root@VM-2-10-ubuntu:/usr/local/redis/db/db1# redis-cli -p 6379 127.0.0.1:6379> keys * 1) "email" 2) "t3" 3) "myt1" 4) "t2" 5) "listest" 6) "t4" 7) "t1" 8) "mynums" 9) "myt2" 127.0.0.1:6379> set t5 "8-15 09:00" OK 127.0.0.1:6379> get t5 "8-15 09:00" 127.0.0.1:6379> set t6 "8-15 09:10" OK 127.0.0.1:6379> exit root@VM-2-10-ubuntu:/usr/local/redis/db/db1# ll total 16 drwxr-xr-x 2 root root 4096 Aug 15 09:00 ./ drwxr-xr-x 8 root root 4096 Aug 12 16:31 ../ -rw-r--r-- 1 root root 346 Aug 15 09:02 appendonly.aof -rw-r--r-- 1 root root 302 Aug 15 09:00 dump.rdb

2,将dump.rdb另一个空白实例6333中,当我们需要只对dump.rdb进行恢复的时候,需要对appendonly设置为no,否则无法恢复;当我们需要对所有的keys进行恢复的时候需要对appendonly.aof进行拷贝已经设置目标端的appendonly为yes。

已知:

6379端口数据目录:/usr/local/redis/db/db1

6333端口数据目录:/usr/local/redis/db/db_6333

root@VM-2-10-ubuntu:/usr/local/redis/db# redis-cli -p 6379 127.0.0.1:6379> save OK 127.0.0.1:6379> keys * 1) "email" 2) "t3" 3) "myt1" 4) "t6" 5) "t2" 6) "listest" 7) "t4" 8) "t1" 9) "mynums" 10) "t5" 11) "myt2" 127.0.0.1:6379> exit root@VM-2-10-ubuntu:/usr/local/redis/db# ll db1 total 16 drwxr-xr-x 2 root root 4096 Aug 15 09:48 ./ drwxr-xr-x 7 root root 4096 Aug 15 09:47 ../ -rw-r--r-- 1 root root 346 Aug 15 09:02 appendonly.aof -rw-r--r-- 1 root root 317 Aug 15 09:48 dump.rdb root@VM-2-10-ubuntu:/usr/local/redis/db# redis-cli -p 6379 127.0.0.1:6379> set t6 "09:54" OK 127.0.0.1:6379> set t7 "09:57" OK 127.0.0.1:6379> exit ### t6是覆盖原来的key,所以现在是一共是12个key root@VM-2-10-ubuntu:/usr/local/redis/db# ll db1 total 16 drwxr-xr-x 2 root root 4096 Aug 15 09:48 ./ drwxr-xr-x 7 root root 4096 Aug 15 09:47 ../ -rw-r--r-- 1 root root 410 Aug 15 09:54 appendonly.aof ##包括后面两次更改的操作 -rw-r--r-- 1 root root 317 Aug 15 09:48 dump.rdb ##更改前的数据存储。 root@VM-2-10-ubuntu:/usr/local/redis/db# ll db_6333/ total 8 drwxr-xr-x 2 root root 4096 Aug 15 09:54 ./ drwxr-xr-x 8 root root 4096 Aug 15 09:54 ../ root@VM-2-10-ubuntu:/usr/local/redis/db# cp db1/dump.rdb db_6333/ root@VM-2-10-ubuntu:/usr/local/redis/db# cat /usr/local/redis/etc/redis_6333.conf |grep ^appendonly appendonly no root@VM-2-10-ubuntu:/usr/local/redis/db# redis-server /usr/local/redis/etc/redis_6333.conf root@VM-2-10-ubuntu:/usr/local/redis/db# redis-cli -p 6333 127.0.0.1:6333> keys * 1) "t5" 2) "t4" 3) "t6" 4) "t1" 5) "t2" 6) "myt1" 7) "myt2" 8) "listest" 9) "t3" 10) "mynums" 11) "email" 127.0.0.1:6333>

这种只恢复了dump.rdb文件,恢复dump.rdb和appendonly.aof文件:

root@VM-2-10-ubuntu:/usr/local/redis/db# redis-cli -p 6333 shutdown root@VM-2-10-ubuntu:/usr/local/redis/db# cp db1/appendonly.aof db_6333/ root@VM-2-10-ubuntu:/usr/local/redis/db# cat /usr/local/redis/etc/redis_6333.conf |grep ^appendonly appendonly yes root@VM-2-10-ubuntu:/usr/local/redis/db# redis-server /usr/local/redis/etc/redis_6333.conf root@VM-2-10-ubuntu:/usr/local/redis/db# redis-cli -p 6333 127.0.0.1:6333> keys * 1) "email" 2) "t1" 3) "myt2" 4) "t2" 5) "myt1" 6) "t5" 7) "t7" 8) "listest" 9) "mynums" 10) "t6" 11) "t3" 12) "t4" 127.0.0.1:6333> get t6 "09:54" 127.0.0.1:6333> get t7 "09:57" 127.0.0.1:6333> exit
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论