Skip to content

How weve saved 98 percent in cloud costs by writing our own database #175

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,97 @@
---
title: How we’ve saved 98% in cloud costs by writing our own database
title: 通过编写自己的数据库,我们节省了98%的云成本
date: 2024-06-08T09:01:47.183Z
authorURL: ""
originalURL: https://hivekit.io/blog/how-weve-saved-98-percent-in-cloud-costs-by-writing-our-own-database/
translator: ""
reviewer: ""
---

#### Cloud
<img src="https://hivekit.io/blog/how-weve-saved-98-percent-in-cloud-costs-by-writing-our-own-database/title.png" alt="两名机械师正在维护数据库" width="200" height="200">

<!-- more -->
编程的第一条规则是什么?可能是类似"不要重复自己"或"如果它能工作,就不要动它"?或者,"不要编写自己的数据库!"……这是一条很好的规则。

Use Hivekit's API as a fully managed cloud service.
编写数据库是一场噩梦,从原子性、一致性、隔离性和持久性(ACID)要求到分片,再到故障恢复和管理 - 一切都难以置信地困难。

幸运的是,市面上有许多经过数十年打磨且分文不取的优秀数据库。那么,我们为什么会愚蠢到从头开始编写一个呢?

嗯,事情是这样的……
-----------------------

我们运行着一个云平台,同时跟踪数万人和车辆。每次位置更新都会被存储,并可以通过历史 API 检索。

同时连接的车辆数量和它们位置更新的频率随时间变化很大,但拥有大约 13,000 个同时连接,每个连接每秒发送大约一次更新,这是相当正常的。

我们的客户以非常不同的方式使用这些数据。有些用例非常粗略,例如当汽车租赁公司想要显示客户当天行驶路线的轮廓时。这种需求可以用 30-100 个位置点来处理一小时的行程,这将允许我们在存储之前对位置数据进行大量聚合和压缩。

但还有许多其他用例,这不是一个选项。快递公司希望能够重放事故发生前的确切几秒钟。矿山拥有非常精确的现场位置跟踪器,希望生成哪个工人踏入哪个受限区域的报告 - 即使只有半米之差。

所以 - 鉴于我们事先不知道每个客户需要什么级别的粒度,我们存储每一次位置更新。以 13,000 辆车计算,每月有 35 亿次更新 - 而且这个数字只会从这里增长。到目前为止,我们一直使用带有 PostGIS 扩展的 AWS Aurora 进行地理空间数据存储。但 Aurora 每月已经花费我们超过 1 万美元,仅用于数据库 - 而且未来只会变得更加昂贵。

但这不仅仅是关于 Aurora 的定价。虽然 Aurora 在负载下表现相当不错,但我们的许多客户正在使用我们的本地版本。在那里,他们必须运行自己的数据库集群,这些集群很容易被这种数量的更新所淹没。

为什么我们不直接使用专为地理空间数据构建的数据库?
-----------------------------------------------------------------------

不幸的是,没有这样的东西。(如果有,而我们在研究中忽略了它,[请告诉我][3])。从 Mongo 和 H2 到 Redis 的许多数据库都支持空间数据类型,如点和区域。还有"空间数据库" - 但它们完全是建立在现有数据库之上的扩展。[PostGIS][4],建立在 PostgreSQL 之上,可能是最著名的一个,但还有其他如[Geomesa][5],它们在其他存储引擎之上提供了出色的地理空间查询能力。

不幸的是,这不是我们需要的。

以下是我们的需求概况:

* **极高的写入性能**
我们希望每个节点每秒能够处理多达 30,000 次位置更新。它们可以在写入前缓冲,从而导致 IOPS 数量大大降低。
* **无限并行性**
多个节点需要能够同时写入数据,没有上限
* **磁盘上的小尺寸**
考虑到数据量,我们需要确保它在磁盘上占用尽可能少的空间

这意味着,我们必须接受一些权衡。以下是我们可以接受的:

* **从磁盘读取的中等性能**
我们的服务器是围绕内存架构构建的。实时流的查询和过滤针对内存中的数据运行,因此非常快。
只有当新服务器上线,当客户使用历史 API 或(即将推出)当应用用户在我们的数字孪生界面上回溯时间时,才会从磁盘读取。这些磁盘读取需要足够快以提供良好的用户体验,但它们相对不频繁且数量较少。

* **低一致性保证**
我们可以接受丢失一些数据。我们在写入磁盘之前缓冲大约一秒钟的更新。在服务器宕机而另一个接管的罕见情况下,我们可以接受丢失当前缓冲区中的那一秒钟的位置更新。


我们需要存储什么样的数据?
--------------------------------------

我们需要持久化的主要实体类型是"对象" - 基本上是任何车辆、人员、传感器或机器。对象有一个 ID 标签、位置和任意的键/值数据,例如燃油水平或当前骑手 ID。位置由经度、纬度精度、速度、方向、高度和高度精度组成 - 尽管每次更新可能只改变这些字段的一个子集。

此外,我们还需要存储区域、任务("对象"必须执行的事情)和指令(hivekit 服务器基于传入数据执行的微小空间逻辑)。

我们构建了什么
----------------

我们创建了一个专用的进程内存储引擎,它是我们核心服务器可执行文件的一部分。它写入一种最小的、基于增量的二进制格式。单个条目看起来像这样:

![图片2:字节图][2]

每个块代表一个字节。标记为"flags"的两个字节是一系列是/否开关,指定"有纬度"、"有经度"、"有数据"等 - 告诉我们的解析器在条目的剩余字节中寻找什么。

我们每 200 次写入存储一次对象的完整状态。在这些之间,我们只存储增量。这意味着,一个完整的位置更新,包括时间和 ID、纬度和经度,只需要 34 个字节。这意味着,我们可以将大约 3000 万个位置更新塞进一个千兆字节的磁盘空间。

我们还维护一个单独的索引文件,将每个条目的静态字符串 ID 及其类型(对象、区域等)转换为唯一的 4 字节标识符。由于我们知道这个固定大小的标识符总是每个条目的字节索引 6-9,因此检索特定对象的历史记录非常快。

结果:云成本减少 98%,一切都更快
---------------------------------------------------------------

这个存储引擎是我们服务器二进制文件的一部分,所以运行它的成本没有变化。但变化的是,我们用每月 200 美元的弹性块存储(EBS)卷替换了每月 1 万美元的 Aurora 实例。我们使用具有 3000 IOPS 的预配置 IOPS SSD(io2),并将更新批处理为每个节点和领域每秒一次写入。

EBS 内置了自动备份和恢复功能,并有高可用性保证,所以我们不觉得我们错过了 Aurora 提供的任何可靠性保证。我们目前每月产生约 100GB 的数据。但是,由于客户很少查询超过 10 天的条目,我们已经开始将所有超过 30GB 的数据移至 AWS Glacier,从而进一步降低我们的 EBS 成本。

但这不仅仅是成本。通过文件系统写入本地 EBS 比写入 Aurora 要快得多,开销也更低。查询也变得快了很多。很难量化,因为查询并不完全类似,但例如,重新创建领域历史中的特定时间点从大约两秒减少到约 13 毫秒。

当然,这是一个不公平的比较,毕竟,Postgres 是一个具有表达性查询语言的通用数据库,而我们构建的只是一个游标流式传输二进制文件,功能非常有限 - 但话又说回来,这正是我们需要的功能,我们没有失去任何特性。

你可以在[https://hivekit.io/developers/][6]了解更多关于 Hivekit 的 API 和功能

[1]: https://hivekit.io/blog/how-weve-saved-98-percent-in-cloud-costs-by-writing-our-own-database/title.png
[2]: https://hivekit.io/blog/how-weve-saved-98-percent-in-cloud-costs-by-writing-our-own-database/byte-diagram.png
[3]: mailto:[email protected]
[4]: https://postgis.net/
[5]: https://www.geomesa.org/
[6]: https://hivekit.io/developers/