暗淡青春|宕机后,Redis如何实现快速恢复?

对 Redis 来说 , 它实现类似照片记录效果的方式 , 就是把某一时刻的状态以文件的形式写到磁盘上 , 也就是快照 。 这样一来 , 即使宕机 , 快照文件也不会丢失 , 数据的可靠性也就得到了保证 。 这个快照文件就称为 RDB 文件 , 其中 , RDB 就是 Redis DataBase 的缩写 。
和 AOF 相比 , RDB 记录的是某一时刻的数据 , 并不是操作 , 所以 , 在做数据恢复时 , 我们可以直接把 RDB 文件读入内存 , 很快地完成恢复 。 听起来好像很不错 , 但内存快照也并不是最优选项 。 为什么这么说呢?
我们还要考虑两个关键问题:

  • 对哪些数据做快照?这关系到快照的执行效率问题;
  • 做快照时 , 数据还能被增删改吗?这关系到 Redis 是否被阻塞 , 能否同时正常处理请求 。
这么说可能你还不太好理解 , 我还是拿拍照片来举例子 。 我们在拍照时 , 通常要关注两个问题:
  • 如何取景?也就是说 , 我们打算把哪些人、哪些物拍到照片中;
  • 在按快门前 , 要记着提醒朋友不要乱动 , 否则拍出来的照片就模糊了
给哪些内存数据做快照?
Redis 的数据都在内存中 , 为了提供所有数据的可靠性保证 , 它执行的是全量快照 , 也就是说 , 把内存中的所有数据都记录到磁盘中 , 这就类似于给 100 个人拍合影 , 把每一个人都拍进照片里 。 这样做的好处是 , 一次性记录了所有数据 , 一个都不少 。
当你给一个人拍照时 , 只用协调一个人就够了 , 但是 , 拍 100 人的大合影 , 却需要协调 100 个人的位置、状态 , 等等 , 这当然会更费时费力 。 同样 , 给内存的全量数据做快照 , 把它们全部写入磁盘也会花费很多时间 。 而且 , 全量数据越多 , RDB 文件就越大 , 往磁盘上写数据的时间开销就越大 。
【暗淡青春|宕机后,Redis如何实现快速恢复?】对于 Redis 而言 , 它的单线程模型就决定了 , 我们要尽量避免所有会阻塞主线程的操作 , 所以 , 针对任何操作 , 我们都会提一个灵魂之问:“它会阻塞主线程吗?”RDB 文件的生成是否会阻塞主线程 , 这就关系到是否会降低 Redis 的性能 。
Redis 提供了两个命令来生成 RDB 文件 , 分别是 save 和 bgsave 。
  • save:在主线程中执行 , 会导致阻塞;
  • bgsave:创建一个子进程 , 专门用于写入 RDB 文件 , 避免了主线程的阻塞 , 这也是 Redis RDB 文件生成的默认配置 。
好了 , 这个时候 , 我们就可以通过 bgsave 命令来执行全量快照 , 这既提供了数据的可靠性保证 , 也避免了对 Redis 的性能影响 。
接下来 , 我们要关注的问题就是 , 在对内存数据做快照时 , 这些数据还能“动”吗? 也就是说 , 这些数据还能被修改吗?这个问题非常重要 , 这是因为 , 如果数据能被修改 , 那就意味着 Redis 还能正常处理写操作 。 否则 , 所有写操作都得等到快照完了才能执行 , 性能一下子就降低了 。
快照时数据能修改吗?
在给别人拍照时 , 一旦对方动了 , 那么这张照片就拍糊了 , 我们就需要重拍 , 所以我们当然希望对方保持不动 。 对于内存快照而言 , 我们也不希望数据“动” 。
举个例子 。 我们在时刻 t 给内存做快照 , 假设内存数据量是 4GB , 磁盘的写入带宽是 0.2GB/s , 简单来说 , 至少需要 20s(4/0.2 = 20)才能做完 。 如果在时刻 t+5s 时 , 一个还没有被写入磁盘的内存数据 A , 被修改成了 A’ , 那么就会破坏快照的完整性 , 因为 A’不是时刻 t 时的状态 。 因此 , 和拍照类似 , 我们在做快照时也不希望数据“动” , 也就是不能被修改 。
但是 , 如果快照执行期间数据不能被修改 , 是会有潜在问题的 。 对于刚刚的例子来说 , 在做快照的 20s 时间里 , 如果这 4GB 的数据都不能被修改 , Redis 就不能处理对这些数据的写操作 , 那无疑就会给业务服务造成巨大的影响 。


推荐阅读