基准测试:Postgres vs.MongoDB

我当着 CTO 的面大规模删除了我们的 MongoDB 集群。

基准测试:Postgres vs.MongoDB
AI编程/Vibe Coding 遇到问题需要帮助的,联系微信 ezpoda,免费咨询。

他的咖啡杯停在半空中,嘴都张开了。仪表板上十四个微服务变黄了。Slack 炸了。我只是坐在那里微笑,因为我花了三周时间证明这是正确的决定。

关于 MongoDB,没人告诉你这件事:我们在 2019 年选择它,是因为一位会议演讲者说"无模式是未来"。那位演讲者现在在一家 Postgres 公司工作。我没有编造这个。

五年来,我们运行了两个数据库。Postgres 用于"严肃的"关系型数据。Mongo 用于事件、日志,以及任何我们称之为"非结构化"的数据,因为我们太懒了,懒得设计合适的模式。两个连接池在消耗内存。两个备份计划需要维护。每个季度都会把我们吵醒的两个独立事件。

然后一位初级开发者问了一个让我崩溃的问题:"我们需要两个数据库吗?"

我张嘴想要解释。什么也没说出来。我没有答案。五年的架构决策,我无法为其中任何一个辩护。

所以我做了任何工程师在假设崩溃时都会做的事情。我编写基准测试来证明自己是对的。

基准测试证明我错了。而且非常残酷。

1、改变一切的那次测试

两个数据库使用相同的硬件。相同的数据集。1000 万个事件文档,看起来像这样:

{
  "user_id": "u_8x7k2m",
  "event": "page_view",
  "metadata": {
    "url": "/products/widget",
    "device": "mobile",
    "referrer": "google.com"
  },
  "tags": ["organic", "returning"]
}

嵌套对象。数组。动态字段。所有 MongoDB 据称为此设计的东西。

我运行了我们在生产环境中实际使用的查询。从我们的应用程序日志中提取的真实查询。

按用户 ID 简单查找:

┌──────────────────┬────────────┬────────────┐
│    Database      │  Avg (ms)  │  P99 (ms)  │
├──────────────────┼────────────┼────────────┤
│ Postgres JSONB   │    0.8     │    2.1     │
│ MongoDB          │    1.2     │    4.7     │
└──────────────────┴────────────┴────────────┘

Postgres 更快。在文档查询上。对抗一个专门为文档构建的数据库。

我又运行了一次。同样的结果。我调整了 MongoDB 的 WiredTiger 缓存。我阅读了 2017 年的论坛帖子。我乞求数字证明我是对的。

它们拒绝了。

嵌套字段查询:

┌──────────────────┬────────────┬────────────┐
│    Database      │  Avg (ms)  │  P99 (ms)  │
├──────────────────┼────────────┼────────────┤
│ Postgres JSONB   │    12.4    │    28.3    │
│ MongoDB          │    14.1    │    31.8    │
└──────────────────┴────────────┴────────────┘

仍然是 Postgres。差距缩小了但从未反转。

数组包含操作:

┌──────────────────┬────────────┬────────────┐
│    Database      │  Avg (ms)  │  P99 (ms)  │
├──────────────────┼────────────┼────────────┤
│ Postgres JSONB   │    18.7    │    42.1    │
│ MongoDB          │    16.2    │    38.4    │
└──────────────────┴────────────┴────────────┘

Mongo 终于赢了一次。领先 2.5 毫秒。在数组操作上。它存在的全部理由。

聚合管道与原始 SQL:

┌──────────────────┬────────────┐
│    Database      │  Time (s)  │
├──────────────────┼────────────┤
│ Postgres JSONB   │    3.2     │
│ MongoDB          │    4.8     │
└──────────────────┴────────────┘

甚至都不接近。SQL 在分析查询上彻底摧毁了聚合框架。

2、真正令人痛苦的部分

基准测试很尴尬。但真正的痛苦来自于我检查我们的事件模式历史时。

我们选择 MongoDB 是为了"灵活性"。无需迁移的模式更改。可以自由演化的文档。

我们的事件模式两年内没有改变过。一次都没有。我们一直在为从未实际使用过的灵活性支付运营复杂性税收。

每个文档都有完全相同的结构。我们整个时间都可以使用带有类型化列的常规 Postgres 表。

我围绕一个从未到来的假设未来大规模配置了整个基础设施。五年的不必要复杂性,因为我相信一个会议演讲胜过相信自己的测量。

3、现在的架构

                    ┌─────────────────────┐
                    │    Application      │
                    └──────────┬──────────┘
                               │
          ┌────────────────────┼────────────────────┐
          ▼                    ▼                    ▼
   ┌─────────────┐      ┌─────────────┐      ┌─────────────┐
   │  Relational │      │    JSONB    │      │  Full-Text  │
   │   Tables    │      │   Events    │      │   Search    │
   └─────────────┘      └─────────────┘      └─────────────┘
                               │
                    ┌──────────┴──────────┐
                    │     PostgreSQL      │
                    └─────────────────────┘

一个数据库。一个备份策略。一个连接池。在事故期间只需调查一件事而不是两件事。

迁移脚本几乎简单得令人侮辱:

cursor = mongo_db.events.find().batch_size(10000)
batch = []

for doc in cursor:
    batch.append(doc)
    if len(batch) >= 10000:
        pg_cur.executemany(
            "INSERT INTO events (data) VALUES (%s)",
            [(Json(d),) for d in batch]
        )
        batch = []
        pg_conn.commit()

四个小时移动 1000 万个文档。基础设施成本下降了 40%。数据库事故下降到零。新工程师不再问为什么我们需要两个数据库,因为我们不再需要了。

4、不舒适的真相

MongoDB 不是糟糕的数据库。它是一个大多数团队实际上并不需要的专门工具。

如果你在 50 台机器上进行分片,拥有结构差异巨大的真正多态文档,MongoDB 是有意义的。如果你有一个深入了解 Mongo 的团队,并且你的文档确实彼此不同,继续使用它。

但如果你在运行一个副本集,拥有数百万个看起来完全相同的文档,你正在为永远不会使用的复杂性付费。

那个提出了不适问题的初级开发者?她现在领导着我们的数据平台。

那位在我删除集群时咖啡变凉的 CTO?他在两个月后批准了我的晋升。

有时最资深的技术决策是承认多年前你做出了错误的决定。检查你的假设。运行基准测试。

工程最难的部分不是构建系统。而是承认你构建了错误的系统。


原文链接: JSONB Is Faster Than You Think: We Benchmarked Postgres vs. Mongo

汇智网翻译整理,转载请标明出处