立即领取|华润集团、宁德核电、东风岚图等 20+ 标杆企业数字化人才培养实践案例 了解详情
写点什么

Nextdoor 零停机 Redis 迁移实践

  • 2020-03-04
  • 本文字数:3012 字

    阅读完需:约 10 分钟

Nextdoor零停机Redis迁移实践

背景介绍

在 Nextdoor,我们大量使用了 Redis 这个高性能的内存数据存储,为我们的许多服务提供低延迟的数据访问。现在,我们通过 ElastiCache 使用着十几个不同的 Redis 实例;其中有许多对应于我们的微服务或我们的主要应用 Django 中的特定用例,比如 AB 测试跟踪。它们可以作为数据的真实来源,也可以纯粹作为缓存来提升性能。


除了少数例外,我们大多数的 Redis 部署最初创建时都是主副本节点的单一实例,这样,我们可以保证单个节点的故障不会造成数据的永久损失,同时,我们可以在故障转移时继续提供服务。此外,通过将特定的用例隔离到它们自己的 Redis 实例,我们还可以实现不同的故障模式,限制单个问题的影响。虽然这听起来很像高可用性数据存储的要素,但我们意识到,我们还没有完全做到这一点,特别是当我们的一些实例达到了其负载能力的极限时。在这篇文章中,我们将解释可用性方面的一些缺陷,这些缺陷最终导致我们重新构建我们的整个实例集,以及我们用来监督这个迁移过程使其不影响任何核心服务的方法。


单一主副本配置的不足之处

例如,让我们考虑这样一个场景:一个 Redis 实例由一个主节点和一个副本节点组成,在接近 CPU 最大处理能力时触发了故障转移(可能是副本节点遇到了硬件问题,需要准备一个新节点)。主节点现在开始获取其自身的快照,并将其发送给新的副本节点,这将导致主服务器达到最大处理能力。此时,到这个 Redis 实例的请求开始超时,可能一些用户开始看到功能受限。我们甚至可以更进一步;如果我们的 Redis 客户端不够智能,无法恰当地处理这些超时,可能会引起连锁反应,我们可能会备份关键服务的所有流量。


虽然这只是一个特殊的例子,但我们已经确定了一组使我们的 Redis 配置变得脆弱的关键的底层问题。一方面,CPU 利用率成为保证可用性的一个重大瓶颈,这不适合那些一天中经常出现突发事件和流量峰值的服务。然而,更重要的是,没有一种简洁的方法可以用来扩展我们的 Redis 实例以满足我们的服务需求(只能购买更大的实例类型,但这不是一个很有远见的解决方案)。


幸运的是,这类问题相对比较常见,而 Redis 本身提供了一种可以解决这个问题的集群模式。使用集群,我们的数据分布在多个分片上,每个分片拥有整个键空间的一部分。从可用性的角度来看,这是有好处的,因为它允许我们在必要时水平地扩展我们的 Redis 实例(只需向集群添加新节点),而不需很长的停机时间。此外,上述重大问题的影响也有所减轻;如果一个分片达到了 CPU 处理能力,那么剩下的分片可以继续正常运行,因此,问题仅会影响一小部分请求。

制定迁移策略

随着规模的不断扩大,我们开始着手升级我们所有的 Redis 部署,以下是几个需要优先考虑的事项:


  • 切换到集群化 Redis:这可以满足我们的水平扩展需求,并从根本上解决上述所有问题。

  • 升级旧的实例类型:我们的大多数 Redis 实例都错过了与新硬件结合的性能提升,因此升级可以进一步缓解我们对高 CPU 利用率的担忧。

  • 尽量减少迁移过程的停机时间:由于我们的许多 Redis 实例都是我们的核心服务的组成部分,所以迁移到新实例的过程中不影响会员的体验很重要。


在这里,我们遇到了最大的挑战:弄清楚迁移的过程。虽然我们非常清楚我们的最终状态应该是什么,但是,最后一个要求——最小化停机时间——排除了许多我们可能最终用来切换到新实例的方法。值得注意的是,我们迁移到集群 Redis 的目标意味着我们可能无法直接通过 AWS,因为 AWS 只支持在不更改集群模式的情况下升级引擎版本(例如,从非集群到非集群或从集群到集群)。为此,我们最好的选择是手动创建一个新的单分片的 Redis 集群,从旧实例的备份中恢复它,然后在稍后扩展分片的数量——在几个小时的迁移期间里不能接受写流量。为了实现这三个目标,我们需要一点创造力来帮助我们实现迁移。


大约在这个时候,我们刚刚开始研究 Envoy 的用法,这是 Lyft 构建的一个分布式代理,它为基于微服务的大型架构提供了更好的可观测性。Envoy 使我们能够在服务中添加故障注入和健康检查等功能,从而可以测试推送的稳定性,只需将其与这些服务一起部署即可。它还可以作为一个 Redis 代理,更具体地说,它可以跨不同的上游集群处理 Redis 请求的路由。在这里,我们找到了大规模 Redis 迁移所需的完美抽象;通过将 Envoy 作为中介添加到所有的 Redis 实例,我们可以更智能地协调将数据迁移到新的 Redis 集群的过程,同时继续为用户提供服务。为了说明这一点,让我们按顺序看下从旧实例切换到新实例所需的全部事件。

第一步:将所有流量代理到旧实例


Envoy 作为我们服务的跨斗(sidecar)Docker 容器运行,我们只需修改服务,将 Envoy 视为其上游 Redis 主机。Envoy 负责侦听 Redis 请求的特定端口,它直接将请求(读和写)转发给实际的旧 Redis 实例。重要的是,除了修改 Redis 的地址外,不需要在客户端进行任何更改,客户端基本上不知道它的请求不会直接发送到 Redis(一个例外是原来的 Redis 实例已经集群化;在这种情况下,客户端可能需要改成使用普通的 Redis 客户端库,而不是集群化 Redis 库,因为 Envoy 的行为类似于“非集群化 Redis”)。

第二步:将写入镜像到新集群


在根据必要的规范配置好新的 Redis 集群之后,我们修改了 Envoy 的 Redis 侦听器定义,将其镜像或双写到新的集群化 Redis。这样,任何新的写入都可以同时写到旧的和新的 Redis 实例。请注意,Envoy 采用了双重写入的即发即弃模型,因此,它不会等着确认新集群的写入是否成功,但通常这里很少出现任何问题。这时,新的 Redis 集群仍然远远落后于旧的集群,但是我们已经比较确定,在实际同步之后,这两个集群不会再次出现差异。

第三步:将内容完整地回填到新集群


这一步是计算最密集的,需要扫描旧的 Redis 实例并将其所有内容复制到新实例。为了更有效率,我们使用了 RedisShake(一个来自阿里巴巴的优秀开源工具),并围绕它封装了我们自己的 ECS 服务来自动同步。请注意,与捕获快照非常相似,运行此脚本也有挂载 CPU 的能力,因此必须特别注意,使用合理的 QPS(即每秒查询次数)和设置来运行脚本,并且最好在低流量期间运行脚本。现在,这两个实例应该完全同步了,任何错过的双写都应该被捕获了。

第四步:验证并切换


为了确保这两个实例是同步的,我们运行 RedisFullCheck 工具,它经过几轮比较后可以区分旧实例和新实例之间存在差异的键,并将它们存储到 SQLite 数据库中。在我们的例子中,键的差异更可能是因为某个地方某个特定的 Redis 用法与 Envoy 不兼容(下面将描述这些不兼容),而不是因为回填脚本遗漏了写操作;这一步可能需要一些调查工作。一旦检查脚本结果没有问题,我们最后会再次更新 Envoy 侦听器配置,将所有的流量全部路由到新集群,并删除旧实例。

一些注意事项

使用上面的策略,我们成功地将所有的 Redis 实例迁移到一个最终状态,其中每个实例都是集群化的,并运行最新版本的 Redis。然而,在实践中,我们发现这种方法也有其局限性,特别是在 Envoy 的使用上。最重要的是,我们发现,Envoy 只支持散列到一个分片的 Redis 请求,它排除了所有与集群相关的命令(由于我们的大多数实例以前都不是集群化的,所以我们很幸运,没有很多这样的命令)。因而使用 Redis MULTI 命令的任何事务逻辑也不受支持。为了解决这个问题,我们修改了基于事务的代码,改为使用 Lua 脚本,它以与 Envoy 兼容的方式提供了相同的原子性优势。

英文原文

Data Migrations Don’t Have to Come with Downtime


2020-03-04 12:006507
用户头像
小智 让所有人认同的文字称不上表达

发布了 408 篇内容, 共 388.2 次阅读, 收获喜欢 1980 次。

关注

评论

发布
暂无评论
发现更多内容

TiDB+TiSpark部署--安装,扩缩容及升级操作

TiDB 社区干货传送门

安装 & 部署

Lua脚本在Redis事务中的应用实践

京东科技开发者

数据库 redis 事务 开发语言 Lua脚本

腾讯云,DevOps 领导者!

CODING DevOps

腾讯云 DevOps IDC CODING

智能电饭煲

OpenHarmony开发者

OpenHarmony

高精度的“文件转换excel”背后藏着这些解题思路!

合合技术团队

人工智能 表格识别

作为一个菜鸟前端开发,面了20+公司之后整理的面试题

beifeng1996

前端 React

这样Debug,排查问题效率大大提升...

程序知音

【存疑】爬虫学习中decode问题

Sher10ck

存疑

为什么Java中有三种基础的类加载器?

小小怪下士

Java 编程 程序员 程序

一个代码仓库(免费)与技术点 的故事

八点半的Bruce.D

GitHub Linux 网络服务 GitHub仓库

长安链ca 容器部署(解决无法访问Mysql问题)

长安链

老生常谈!数据库如何存储时间?你真的知道吗?

小小怪下士

Java 数据库 编程 程序员

2022年面试复盘大全500道:Redis+ZK+Nginx+数据库+分布式+微服务

小小怪下士

数据库 redis 分布式 微服务 java面试

爆肝整理5000字!HTAP的关键技术有哪些?| StoneDB学术分享会#3

StoneDB

数据库 HTAP StoneDB 企业号九月金秋榜 9月月更

架构师成长之路——什么是架构师

小小怪下士

Java 程序员 架构 后端

前端面试哪些是必须要掌握的

loveX001

JavaScript 前端

开发者问第四期|统一扫码服务、机器学习服务等问题解答

HarmonyOS SDK

蓝海变红海,NFT 的未来在哪里

TinTinLand

区块链 创业 web3 NFT生态链游

2022最新腾讯面经分享:Java 面试刷题 PDF(17 大专题 )

Java-fenn

Java 编程 程序员 面试 java面试

元宇宙场景技术实践|虚拟直播间搭建教程

ZEGO即构

音视频开发 元宇宙 虚拟直播

数字化办公,企业OA软件技术该如何发力?

FinClip

EasyCV带你复现更好更快的自监督算法-FastConvMAE

阿里云大数据AI技术

深度学习 算法 计算机视觉

云图说丨DDoS防护解决方案:DDoS大流量攻击防得住

华为云开发者联盟

云计算 后端 华为云 企业号九月金秋榜

35岁程序员自荐:我所掌握的架构技术

小小怪下士

Java 程序员 中年危机

2.69分钟完成BERT训练!新发CANN 5.0加持

华为云开发者联盟

人工智能 企业号九月金秋榜

模块一

早安

极客时间架构训练营

基于云原生技术打造全球融合通信网关

阿里云视频云

云原生 网络 通信 通信云

ESP32-C3入门教程 基础篇(八、NVS — 非易失性存储库的使用)

矜辰所致

ESP32-C3 9月月更 NVS

英特尔Wi-Fi 7速率提升5倍,为多应用场景带来改变

科技之家

打破联接壁垒,华为云IoT到底强在哪?

华为云开发者联盟

云计算 后端 物联网 华为云 企业号九月金秋榜

中国DevOps平台市场,华为云再次位居领导者位置

华为云开发者联盟

云计算 华为云 企业号九月金秋榜

Nextdoor零停机Redis迁移实践_架构_Sameer Suresh_InfoQ精选文章