东亚银行、岚图汽车带你解锁 AIGC 时代的数字化人才培养各赛道新模式! 了解详情
写点什么

放弃 Python,Uber 用 Go 重写 Schemaless 数据库的分片层

Anders Johnsen

  • 2018-11-19
  • 本文字数:3086 字

    阅读完需:约 10 分钟

放弃 Python,Uber 用 Go 重写 Schemaless 数据库的分片层

摘要: 2014 年,Uber 构建了可扩展的容错数据库 Schemaless,但随着业务的增长,原实现方式对资源消耗更多,同时请求延迟也在增加,为了保持 Schemaless 的性能,Uber 在不影响生产服务的情况下用 Go 重写了 Schemaless 数据库的分片层,完成了将产品系统从旧实现迁移到新实现的 Frontless 项目。



2014 年,Uber 工程构建了可扩展的容错数据库Schemaless,为公司的快速发展提供了便利。我们仅在 2016 年就部署了 40 多个 Schemaless 实例和数千个存储节点。


随着业务的增长,我们的资源消耗和延迟也在增长;为了保持 Schemaless 的性能,我们需要一个能够很好的支持大规模应用的解决方案。在明确了假如将现有 Schemaless“集群”的 Python 工作节点用Go(一种支持轻量级并发特性的语言)重写的话,我们的数据库可以获得显著的性能提升后,我们在不影响正常生产的情况下,完成了将产品系统从旧实现迁移到新实现的任务。这一任务被称为 Frontless 项目,它证明了我们可以在不影响生产服务的情况下重写大型数据库的前端。


在本文中,我们会讨论如何将 Schemaless 分片层从 Python 迁移到 Go,这一改变可以使我们用更少的资源来处理更多的流量,从而改善用户对我们服务的体验。

Schemaless 的背景

作为Mezzanine项目,Schemaless 于 2014 年 10 月首次推出,当初计划将 Uber 的核心 trip 数据库从一个独立的Postgres实例迁移到一个高可用的数据库中。


包含核心 trip 数据的 Mezzanine 数据库被构建为第一个 Schemaless 实例。从那时算起,目前已经部署了 40 多个 Schemaless 实例用于众多客户端服务。(关于我们内部数据库的完整历史演进过程,请参阅我们的三篇系列文章,Schemaless 的设计架构triggers概述)。


在 2016 年中,有数千个工作节点在 Schemaless 实例中运行,每个工作节点都消耗大量的资源。工作节点最初是使用 Python 和由NGINX交付的uWSGI应用程序服务器进程中的一个Flask微框架构建的,每个 uWSGI 进程一次处理一个请求。


该模型简单易行,易于建立,但不能有效地满足我们的需求。为了处理额外的同步请求,我们必须增加更多的 uWSGI 进程,每个进程都作为一个需要额外开销的新的 Linux 进程,因而这从根本上限制了并发线程的数量。在 Go 中,goroutines 被用来构建并发程序。goroutine 采用轻量级设计,是由 Go 的运行时系统管理的线程。


为了研究重写 Schemaless 分片层的优化增益,我们创建了一个实验性的工作节点,该节点实现了一个使用频率较高、资源消耗也比较高的端点。重写的结果显示,延迟减少了 85%,资源消耗减少的甚至更多。



图 1:该图描述了 Frontless 形式实现的端点中值请求延迟情况


在进行了这个实验之后,我们明确了重写将使 Schemaless 通过释放 CPU 和内存来支持其所有实例中的依赖服务和工作节点。有了这些知识基础,我们启动了这个 Frontless 项目,用 Go 重写整个 Schemaless 分片层。

Frontless 架构设计

为了成功地重写 Uber技术堆栈的这个重要部分,我们需要确保我们的重新实现 100%与现有的工作节点兼容。我们做了一个关键的决定,以验证新实现与原始代码的关系,这意味着每个对新 Go 工作节点的请求都要得到跟之前对 Python 工作节点请求相同的结果。


我们估计一个完整的重写会花费我们六个月的时间。在此期间,在 Uber 的生产系统中实现的新功能和 bug 修复将在 Schemaless 的情况下进行,所以我们的迁移有了一个动态的目标。我们选择了迭代开发形式,这样我们就可以一次性在一个端点上不断的从遗留的 Python 代码库中迁移出功能,并同时在新的 Go 代码库中验证。


最初,Frontless 工作节点只是在现有的 uWSGI Schemaless 工作节点前面的一个代理,所有请求都通过该节点。迭代将从重新实现一个端点开始,然后在生产中进行验证;当不再有错误出现后,新的实现才会正式上线。


从部署的角度来看,Frontless 和 uWSGI Schemaless 的工作是一起构建和部署的,这使得在所有实例中都可以实现统一的 Frontless,并同时支持所有生产场景的验证。



图 2:在我们的迁移过程中,一个名为 worker 节点的服务,其中 Frontless 和 Schemaless 在同一个容器中运行。Frontless 随后收到请求,并决定是否应该将其转发给 Schemaless,或者由 Frontless 处理。最后,Schemaless 或 Frontless 从存储节点获取结果,并将其返回给服务。

读取端点:对比验证

我们首先聚焦在用 Go 重新实现读取端点上。在我们最初的实现中,Schemaless 实例上读取端点处理平均占用 90%的流量,并且它也是最消耗资源的。


当一个端点用 Frontless 实现后,将会启动验证进程,检测与 Python 实现的差异性。Frontless 和 Schemaless 执行请求操作时便会触发验证并对比响应结果。



图 3:当一个服务发送请求到 Frontless 时,它会将请求转发给 Schemaless,该请求将通过查询存储节点生成响应。然后,由 Schemaless 做出的响应将返回到 Frontless,并将其转发给服务。Frontless 还将通过查询存储节点来创建响应。这两种响应是由 Frontless 和 Schemaless 构建的,如果出现任何差异,结果将作为 bug 报告发送给 Schemaless 开发团队。


使用此方法验证,将使发送到存储工作节点的请求数量增加一倍;为了使请求数量增加后工作正常,我们添加了配置标志来激活每个端点的验证,并调整请求验证的百分比阈值。这样便可以在几秒内启动或禁用对指定端点任意部分的验证功能。

写入端点:自动集成测试

Schemaless 的写入请求只能一次性成功,所以为了验证这些我们不能再使用以前的策略了。然而,由于与读取端点相比,在 Schemaless 中写入端点要简单得多,因此我们决定通过自动化集成测试来测试它们。


我们建立起了集成测试环境,这样 Schemaless Python 和 Frontless Go 就可以运行相同的测试场景了。测试是自动化的,可以在本地执行,也可以在几分钟内通过持续的集成来执行,这可以加快开发周期。


为了规模化测试我们的实现,我们设置了一个 Schemaless 测试实例,其中流量测试模拟了生产流量。在这个测试实例中,我们将 Schemaless 的 Python 流量写入实现迁移到了 Frontless 上,并运行验证来确保写入的正确性。


最后,一旦所有实现都满足生产环境时,我们就可以通过运行时配置将 Schemaless 的 Python 实现的流量写入功能缓慢地迁移到 Frontless 上,这样便可以在几秒钟内将部分流量写入工作移动到新的实现中。

Frontless 的成果

到 2016 年 12 月为止,所有的 Mezzanine 数据库都是由 Frontless 处理的。如图 4 所示,所有请求的中值延迟降低了 85%,p99 请求延迟降低了 70%:



图 4:上图展示了由 Python(Schemaless 的工作语言,用红色表示)和 Go(Frontless 的工作语言,用蓝色表示)实现时数据库请求处理的时间。


随着我们 Go 的实现,Schemaless 的 CPU 使用率下降了 85%以上。这种效率的增加让我们减少了在所有 Schemaless 实例中使用工作节点的数量,这些节点也是基于与以前相同的 QPS,这从而提高了节点利用率。



图 5:上面的图展示了在我们的数据库中由 Python(Schemaless 工作语言,红色的)和 Go(Frontless 的工作语言,蓝色的)处理的一个稳定的请求流中的 CPU 使用情况。

Frontless 的未来

Frontless 项目表明,我们有可能在零停机的情况下,用一种全新的语言重写一个关键系统。通过重新实现服务而不改变 Schemaless 的现有客户端,我们能够在几天内而不是数周或几个月内实现、验证和启用端点。重点是,验证过程(新的端点实现与现有生产中的实现进行比较)给了我们信心,因为 Frontless 和 Schemaless 可以得到相同的结果。


然而,最重要的是,我们在生产中重写关键系统的能力证明了 Uber 迭代开发过程的可伸缩性。


原文链接:https://eng.uber.com/schemaless-rewrite/


2018-11-19 09:002071

评论 1 条评论

发布
用户头像
难道我大python没有未来了吗?
2018-11-21 17:33
回复
没有更多了
发现更多内容

使用矢量数据库打造全新的搜索引擎

这我可不懂

数据库 搜索引擎 矢量 矢量索引

windows如何上架ios应用到app store

雪奈椰子

3分钟全面了解API Moke

优测云服务平台

Mock API 编排 Mock 服务 API 接口 API 测试

构建容错软件系统的艺术

高端章鱼哥

负载均衡

信科技AntDB数据库专家参加向量数据库首次技术标准研讨会

亚信AntDB数据库

数据库 AntDB AntDB数据库 企业号 7 月 PK 榜

面向金融科技方向选手!一级学会背书,AI选股与可视分析大赛来啦

飞桨PaddlePaddle

人工智能 百度 paddle 飞桨 百度飞桨

几种常用到的 Hybrid App 技术框架

Onegun

flutter React Native Hybrid

Seata 1.7.0 正式发布,大幅度提升稳定性

阿里巴巴云原生

阿里云 云原生 seata

火山引擎数智平台旗下DataWind升级半年报 6大功能助力企业数据消费

字节跳动数据平台

数据库 大数据 企业号 8 月 PK 榜

证券机构数据治理实践,实现数据的“管、 治、用”

袋鼠云数栈

数字化转型 金融

代码随想录Day23 - 二叉树(九)

jjn0703

重磅发布 | 博睿数据首部全行业IT运维精选案例集

博睿数据

IT 案例分享 博睿数据 精选案例 IT 运维

阿里云率先荣获容器集群稳定性先进级认证

阿里巴巴云原生

阿里云 云原生 容器服务

人人可用的前端低代码框架

这我可不懂

低代码 前端框架 应用开发

华为云盘古大模型辅助药物设计,西交大的新型抗生素研发之路

华为云开发者联盟

人工智能 华为云 华为云开发者联盟 企业号 8 月 PK 榜

作为新手小白,你应该了解的五个3DMAX的使用干货小技巧!

Finovy Cloud

3ds Max

分布式系统常见理论讲解

越长大越悲伤

分布式 后端 一致性

Footprint Analytics 宣布 20+ 链 API 免费增速,助力熊市 buidler

Footprint Analytics

区块链 NFT Footprint

大模型的“海马体”来了!腾讯云AI 原生向量数据库正式上线公测

Geek_2d6073

代码随想录Day35 - 贪心算法(四)

jjn0703

开放原子开源基金会理事长孙文龙一行赴浪潮考察交流

开放原子开源基金会

开源

TE智库 |《中国CLM(合同全周期管理)市场产业全景研究报告》发布,看合同管理赋能企业战略落地

TE智库

高并发与性能优化的神奇之旅

互联网工科生

性能优化 数据 高并发

加强Web应用程序安全:防止SQL注入

树上有只程序猿

sql Web ORM

【AI模型系列】中国AI大模型峰会“封神之作”,不容错过的大模型机遇

颜淡慕潇

AI大模型 峰会 WAVE SUMMIT

直播预告 | 博睿学院:探索ES在日志场景的应用

博睿数据

可观测性 博睿数据 智能运维AIOps 博睿学院

【参赛有奖】云原生编程挑战赛·赛道 2 邀你来战!

阿里巴巴云原生

阿里云 阿里云云原生

什么是多运行时架构?

互联网工科生

分布式 服务化 单机

不断创新短视频APP源码,未来可期!

山东布谷网络科技

App 短视频 短视频源码

轻松构建全栈观测,从容应对咖啡产业竞争

阿里巴巴云原生

阿里云 云原生

大模型真的会“好事多模”吗?

白洞计划

多模态 大模型

放弃 Python,Uber 用 Go 重写 Schemaless 数据库的分片层_软件工程_InfoQ精选文章