HarmonyOS开发者限时福利来啦!最高10w+现金激励等你拿~ 了解详情
写点什么

数据库连接从 15000 降到 100 以下:DigitalOcean 如何解决技术债

  • 2020-03-14
  • 本文字数:3226 字

    阅读完需:约 11 分钟

数据库连接从15000降到100以下:DigitalOcean如何解决技术债

本文最初发布于 DigitalOcean 官方博客,经原作者授权由 InfoQ 中文站翻译并分享。


最近,一位新员工在午餐时问我:“DigitalOcean 的技术债务是什么情况?”


听到这个问题时,我忍不住笑了。软件工程师询问一家公司的技术债务就相当于询问其信用评分。这是他们衡量一家公司过去的遗留问题以及他们所要背负的包袱的方法。DigitalOcean 对技术包袱并不陌生。


作为云提供商,我们管理着自己的服务器和硬件,我们面临着许多其他初创公司在这个云计算的新时代尚未遇到的复杂性。这些艰难的情况最终迫使我们不得不早做权衡。任何快速成长的公司都知道,你早期做出的技术决定往往会在以后影响到你。


盯着桌子对面的新员工,我深吸了一口气,然后开始说。“让我告诉你,我们有 15000 个与数据库的直接连接……”



我给这位新员工讲的故事是 DigitalOcean 迄今为止最大的技术重组。这是整个公司多年来的努力,教会了我们很多东西。我希望,讲述这个故事可以帮助未来的 DigitalOcean 开发者——或任何发现自己陷入棘手的技术债务问题的开发者。

故事缘起

DigitalOcean 从一开始就痴迷于简单性。这是我们的核心价值观之一:力求简单而优雅的解决方案。这不仅适用于我们的产品,也适用于我们的技术决策。这一点在我们最初的系统设计中最为明显。


与 GitHub、Shopify 和 Airbnb 一样,DigitalOcean 也是在 2011 年从一个 Rails 应用程序开始的。我们内部称为 Cloud 的 Rails 应用程序管理着 UI 和公共 API 中的所有用户交互。Rails 服务有两个 Perl 编写的辅助服务:调度器和 DOBE(DigitalOcean 的后端)。调度器将调度并分配 Droplet 给管理程序,而 DOBE 负责创建实际的 Droplet 虚拟机。Cloud 和调度器作为独立的服务运行,而 DOBE 则运行在 fleet 中的每台服务器上。


无论是 Cloud、调度器,还是 DOBE,彼此之间都不能直接对话。他们通过 MySQL 数据库通信。这个数据库有两个作用:存储数据和代理通信。这三个服务都使用单个数据库表作为消息队列来传递信息。


每当用户新建一个 Droplet 时,Cloud 就会向队列插入一个新的事件记录。调度器每秒一次不断地在数据库中轮询新的 Droplet 事件,并将它们的创建工作安排给一个可用的管理程序。最后,每个 DOBE 实例将等待新调度的 Droplet 创建并完成任务。为了能检测到所有新的更改,这些服务器都需要在数据库中轮询表中的新记录。



虽然在系统设计方面,无限循环和让每个服务器直接连接到数据库可能比较低级,但它很简单,而且很有效——特别是在人手短缺的技术团队面临紧迫的期限和快速增长的用户群时。


四年来,数据库消息队列一直是 DigitalOcean 技术的支柱。在此期间,我们采用了微服务体系结构,用 gRPC 代替 HTTPS 进行内部通信,并取消了 Perl,代之以 Golang 作为后端服务。然而,所有的方法都指向 MySQL 数据库。


值得注意的是,不能仅仅因为某些东西是“遗产”就认为它功能不完善,应该被取代。Bloomberg 和 IBM 都有用 Fortran 和 COBOL 编写的遗留服务,这些服务产生的收入超过整个公司。另一方面,每个系统都有一个扩展限制。我们就要达到这个限制了。


从 2012 年到 2016 年,DigitalOcean 的用户流量增长超过了 1000%。我们在目录中添加了更多的产品,在基础设施中添加了更多的服务。这增加了数据库消息队列上进入的事件。对 Droplet 需求增加意味着调度器要加班加点地把它们分配到服务器上。不幸的是,对于调度器,可用服务器的数量不是静态的。



为了满足 Droplet 需求的不断增长,我们不断地增加服务器来处理流量。每个新的管理程序都意味着一个新的数据库持久连接。截至 2016 年初,该数据库拥有超过 1.5 万个直接连接,每一个连接每 1 到 5 秒就查询一次新事件。如果这还不够糟糕的话,每个管理程序用来获取新 Droplet 事件的 SQL 查询也变得越来越复杂。它已经变成了一个巨人,有 150 多行,18 张表关联在一起。令人印象深刻的是,它不稳定,而且很难维持。


不出所料,正是在这个时期,问题开始显现。单点故障和成千上万的依赖关系攫取了共享资源,不可避免地导致了一段时间的混乱。表锁和查询积压会导致停机和性能下降。


由于系统的紧耦合,我们没能找到一个清晰简单的解决方案来解决这个问题。Cloud、调度器和 DOBE 都是瓶颈。如果只修补其中一两个组件,就会将负载转移到其余的瓶颈组件上。因此,经过深思熟虑,工程师们想出了一个三管齐下的方案来解决这个问题:


  1. 减少数据库上直接连接的数量;

  2. 重构调度器的排名算法,改进可用性;

  3. 移除数据库的消息队列职责。

开始重构

为了解决数据库依赖,DigitalOcean 的工程师创建了事件路由器。事件路由器充当区域代理,代表每个数据中心中的每个 DOBE 实例轮询数据库。这样,就只有少数代理在做查询,而不是数以千计的服务器。每个事件路由器代理将获取特定区域中的所有活动事件,并将每个事件委派给适当的管理程序。事件路由器还将庞大的轮询查询拆分成更小、更容易维护的轮询查询。



当事件路由器上线后,数据库连接的数量从 15000 个减少到不足 100 个。


接下来,工程师们把目光投向了下一个目标:调度器。如前所述,调度器是一个 Perl 脚本,它决定哪个管理程序将托管一个创建好的 Droplet。它使用一系列查询来对服务器进行排序。每当用户创建一个 Droplet 时,调度器就用最好的机器更新表行。


虽然听起来很简单,但该调度器有一些缺陷。它的逻辑很复杂,很难处理。它是单线程的,在流量高峰期间性能会受影响。最后,该调度器只有一个实例,却必须为整个 fleet 服务。这是一个不可避免的瓶颈。为了解决这些问题,工程团队创建了调度器 V2。


更新后的调度器彻底修改了排名系统。它不从数据库中查询服务器指标,而是从管理程序中聚合它们,并将它们存储在自己的数据库中。此外,调度器团队使用并发和复制保证新服务的负载性能。


事件路由器和调度器 V2 取得了许多了不起的成果,消除了当时的许多架构缺陷。尽管如此,还是有一个明显的障碍。到 2017 年初,集中式 MySQL 消息队列仍然在使用——甚至还很频繁。它每天处理 40 万条新记录,每秒更新 20 次。


遗憾的是,移除数据库消息队列并不容易。第一步是避免服务直接访问它。数据库需要一个抽象层。它还需要一个 API 来聚合请求并代它执行查询。任何服务想要创建一个新事件,都需要通过 API 来实现。于是,Harpoon 诞生了。


不过,为事件队列构建接口这部分比较容易。事实证明,从其他团队那里获得支持更加困难。与 Harpoon 集成意味着团队将不得不放弃他们的数据库访问,重写他们的部分代码库,并最终改变他们一直以来做事的方式。这可不是件容易的事。


Harpoon 的工程师们逐个团队、逐个服务地将整个代码库迁移到他们的新平台上。这花了差不多一年的时间,但到 2017 年底,Harpoon 成为数据库消息队列的唯一发布者。


现在真正的工作开始了。对事件系统的完全控制意味着 Harpoon 可以自由地重新设计 Droplet 工作流了。


Harpoon 的第一个任务是将消息队列职责从数据库提取到自身。为此,Harpoon 创建了自己的内部消息队列,由 RabbitMQ 和异步 worker 组成。当 Harpoon 在一侧把新事件推入队列时,worker 在另一侧拉取它们。由于 RabbitMQ 取代了数据库队列,worker 可以方便地直接与调度器和事件路由器交互。因此,调度器 V2 和事件路由器不是通过轮询从数据库获取新变化,而是由 Harpoon 直接将更新推送给它们。截止到 2019 年本文撰写时,这就是 Droplet 事件架构所处的位置。


一路向前

在过去的 7 年里,DigitalOcean 已经从一家小型创业公司成长为今天这样的成熟的云提供商。与其他转型中的科技公司一样,DigitalOcean 会定期处理遗留代码和技术债务。无论是拆分单体应用、创建多区域服务,还是消除单点故障,我们 DigitalOcean 的工程师们始终致力于打造优雅而简单的解决方案。


这就是我们的基础设施如何随着用户群的发展而扩展的故事,希望你觉得这是个有趣而又有启发性的故事。我很乐意在下面的评论中看到你的想法!


原文链接:


From 15,000 database connections to under 100: DigitalOcean’s tale of tech debt


2020-03-14 10:002071

评论

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

硬核!阿里出品2023版Java架构师面试指南,涵盖Java所有核心技能

三十而立

Java java面试

HummerRisk 使用教程:资源态势

HummerCloud

论文分享丨Holistic Evaluation of Language Models

华为云开发者联盟

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

如何防止AD域环境遭受恶意攻击?

嘉为蓝鲸

AD #运维

gRPC快速整合SpringCloud

Java你猿哥

Java gRPC Spring Cloud 后端 ssm

大数据计算引擎 EasyMR:拥抱开源,引领技术创新

袋鼠云数栈

大数据 大数据基础平台

直击面试!阿里技术官手码12W字面试小册在Github上爆火

Java你猿哥

Java 后端 面经 简历 Java工程师

户外广告屏为什么会坏?

Dylan

LED 户外LED显示屏

一站式管理多平台小程序的办法

Onegun

微信小程序 小程序管理平台 小程序管理

Docker等容器技术应用到移动开发的探索

Onegun

容器 docker build 小程序容器

矩阵佛萨奇(MetaForce)合约开发源码搭建

薇電13242772558

web3

ChatGPT能否取代程序员?仍然是一个需要认真探讨的问题,对此你怎么看?

兴科Sinco

OpenAPI openai #人工智能 ChatGPT

JAVA实战:如何让单元测试覆盖率达到80%甚至以上

Java你猿哥

Java ssm 单元测试 Java工程师 java实战

得物社区计数系统设计与实现

得物技术

性能优化 重构 稳定性

2023年金三银四大厂高频Java面试题(附答案)整理总结

架构师之道

程序员 java面试

低代码开发平台如何推动企业数字化转型

力软低代码开发平台

开发和测试融合,到底该怎么做?

BY林子

敏捷开发 敏捷测试

聊聊池化层和步长为2的卷积层

华为云开发者联盟

人工智能 华为云 华为云开发者联盟 企业号 3 月 PK 榜 卷积层

BugBuilder: 高质量大规模缺陷库自动构建方法

华为云开发者联盟

开发 华为云 补丁 华为云开发者联盟 企业号 3 月 PK 榜

4种API性能恶化根因分析

华为云开发者联盟

开发 API 华为云 华为云开发者联盟 企业号 3 月 PK 榜

2023年一线大厂 Java 面试八股文大全(整理版)附答案详解!

采菊东篱下

Java 程序员 面试

机器学习算法(一): 基于逻辑回归的分类预测

汀丶人工智能

数据挖掘 机器学习 数据分析 逻辑回归

尚硅谷Java真题详解教程发布

小谷哥

延伸测试边界,银行测试团队转型建议

BY林子

软件测试 敏捷测试 测试转型

HummerRisk 使用教程:k8s检测

HummerCloud

【活动报名】 拥抱公平《 Impact Tech, She Can 》

亚马逊云科技 (Amazon Web Services)

人工智能

开门见山|首期《崖山论“见”》技术 Meetup启程

YashanDB

模型训练过程中,混合精度训练稳定性解决方案

Openlab_cosmoplat

模型训练 开源社区

赋能数字经济新动能 焱融科技获评「人工智能高质量发展-行业责任担当」企业

焱融科技

人工智能 文件存储 容器存储 分布式文件存储 全闪存储

综合系统清理优化工具:MacCleaner PRO中文激活版

真大的脸盆

Mac Mac 软件 mac系统清理优化软件 清理工具 清理优化

我眼中的优秀PM

BY林子

团队管理 项目管理

数据库连接从15000降到100以下:DigitalOcean如何解决技术债_数据库_Sunny Beatteay_InfoQ精选文章