跳到内容
Kulicki.tech / Blog
返回

Appwrite vs Supabase — Cloud vs Self-Hosted 性能对比

在 Hetzner 服务器上对 Appwrite 和 Supabase 做详细性能 benchmark,并包含真实 eCommerce 模拟结果。

在这篇文章里,你会读到:

  • Appwrite 性能:Appwrite 处理 eCommerce 模拟有多快
  • Supabase 性能:Supabase 在同样 eCommerce 模拟下有多快
  • Cloud vs Self-Hosted:自托管实例如何超过云选项,并为不同用例提供更好的控制和性能
  • 灵活性:根据性能、灵活性和易用性,Supabase 或 Appwrite 哪个平台更适合你的具体需求
  • 低成本扩展:5-30 欧元服务器在 Supabase 和 Appwrite 自托管配置中可以支持多少用户

到最后,你会清楚理解 Supabase 或 Appwrite 哪个方案能更好帮助你 bootstrap MVP,或者扩展并保持最佳性能,也会知道什么时候选择云托管,什么时候自己搭实例来获得最大灵活性和性能。

另外,我包含了带详细结果的 代码仓库,所以你可以重新跑同样的场景,也可以很容易改成适合你自己用例的版本。

结果可能会让你意外。

没有人偷偷给我们塞钱!

动机

这个对比背后的想法很简单:我想看看 Supabase 和 Appwrite 的表现如何,也想看看它们的 self-hosted 实例 在 预算友好配置 上表现如何。

为了寻找给 app、网站或额外基础设施供电的低成本方案,我决定在便宜硬件上测试这些平台。我的目标是理解这些后端服务在典型 eCommerce 用户模拟下处理得怎么样,以及它们能不能在较低价格下提供可靠性能。这样我就能判断是否可以在预算有限时 bootstrap 一个项目,同时不牺牲性能或可扩展性。

我也有点厌倦所有人都推昂贵又复杂的 AWS setup,最后把你拖进复杂成本结构,轻松超过每月 100 欧元。所以我给你一个能以一小部分成本提供不错性能的方案,每月最多 30 欧元。重点就是把 self-hosting 的价值用到最大,同时不破产。

工具

  • Appwrite:一个处理认证、数据库等功能的后端平台,支持 cloud 和 self-hosted setup
  • Supabase:Firebase 的热门替代品,同样支持 cloud 和 self-hosted setup
  • Grafana k6:强大的负载测试工具,用来测量这些平台如何处理模拟流量
  • Bun 和 ElysiaJS:我会用它们暴露 API 和 Appwrite/Supabase SDK,提供测试时调用的平台接口
  • Mocked database:setup 包含 25,000 条商品数据和 5,000 条用户数据

它们全部都是 open-source,不仅提供强大功能,也有很好的用户社区支持和开发。

ElysiaJS 据说比 Express 快 21 倍。

Hosting

Hetzner Ampere vCPUs

在这个对比的 self-hosting 部分,我使用 Hetzner Cloud Shared vCPUs。 这些是便宜的云机器,按使用分钟计费,并且有最高月度上限。也就是说不管你在服务器上做什么,都不会超过限制。

我使用的服务器基于 Ampere 架构,这个架构以能效著称,操作系统是 Ubuntu。

对于 cloud 测试,我严格使用 Supabase 和 Appwrite 的 free-tier 账户,并保持在它们的使用限制内,用来模拟成本最低的 setup。有意思的是,当你在 cloud 中创建实例时,可以选择与 Hetzner 提供的位置类似的地区。

本文提到的所有价格在 2024 年 9 月 25 日 时有效。

测试场景

测试类型

对于每个 Hetzner 实例,我会测试并找到 breakpoint,也就是服务器无法再处理负载的点。识别出这个点后,我会运行 load test,并确定服务器能够在保持 行业标准响应时间 的情况下运行的最大舒适区。这个过程会对每个服务器实例重复。

为了确保结果准确,我会在每次测试前清空 cache 和 storage,确保一次只运行一个 app 实例。

Ecommerce 模拟

为了模拟基本用户行为,我会安装 Appwrite 或 Supabase,以及各自对应的 API client。我不会以任何方式调优这些工具,因为目标是 评估它们开箱即用的性能,而不是通过微调人为提高 benchmark。核心想法是看这些平台在正常日常条件下表现如何。

接下来,我会把两张表导入服务器数据库:

  • 5,000 个用户,字段包括姓名、地址和电话号码
  • 20,000 个商品,字段包括详情、编号和图片 URL

为了模拟一个 eCommerce 用户,我会实现一个 Virtual User(VU)实例。VU 会按照这个顺序执行:

  1. GET /user:查询用户表,根据精确姓名找到匹配项,并返回对应行
  2. 收到 user 响应后,VU 会立刻调用:
  3. GET /products:通过下面步骤查询商品表:
    • 匹配 name 和 image 等字段
    • 按升序排序结果
    • 限制查询返回特定数量的商品

收到商品数据后,VU 会等待 1 秒,然后重复整个流程。

这个 setup 模拟了常见 eCommerce 流程:用户登录,获取自己的详情,然后拉取过滤后的商品数据用于首页展示。需要注意的是,这个测试关注原始后端性能。在真实应用中,客户端缓存通常会减少负载,但这里我们排除缓存,是为了 stress-test 后端的完整能力。

另外,1 秒循环是刻意激进的,它会持续查询服务器,这并不是典型用户行为。通常登录并拉取数据后,用户会在屏幕上交互几秒钟,然后才发起更多请求。在这里,持续查询代表重负载场景,会进一步推动每个平台的性能极限。

Breakpoint testing

对于 breakpoint testing,我会随着时间逐渐增加 VU 实例数量,使用 ramping executor 观察服务器在递增负载下的表现。测试从 1 个 VU 持续 30 秒开始,给服务器 warm-up 时间,然后我开始逐渐增加负载,从 1 到 50 VU 用 30 秒,再继续增加到 500 VU 以及更高。

测试结束时,目标是让 10,000 VUs 在连续 1 秒循环 中调用两个 endpoint。

这个测试的目标是观察服务器如何响应不断增加的并发用户数,确定 breaking point,也就是服务器在开始出现 timeout、error 或 crash 前能处理的用户数量。这会清楚显示每个平台的扩展能力。

export const options = {
  scenarios: {
    breakpoint: {
      executor: "ramping-vus",
      startVUs: 1,

      stages: [
        { duration: "30s", target: 1 }, // Warm-up
        { duration: "30s", target: 50 },
        { duration: "30s", target: 500 },
        { duration: "30s", target: 1000 },
        { duration: "30s", target: 2000 },
        { duration: "30s", target: 3000 },
        { duration: "30s", target: 5000 },
        { duration: "30s", target: 10000 },
      ],

      gracefulRampDown: "1s",
    },
  },
};

export default () => {
  const user = http.get(URL_USER);
  const products = http.get(URL_PRODUCTS);
  sleep(1);
};

寻找舒适区

为了识别服务器在保持行业标准性能时可以舒服处理的用户数量,我会用固定 VU 数量在一段时间内运行 load test。只有当服务器响应满足以下条件时,测试才通过:

  • 99% 的请求在 1000ms 内返回
  • 95% 的请求在 500ms 内返回
  • 90% 的请求在 200ms 内返回
  • 99% 的响应成功(返回 200 OK)

这些标准确保服务器运行在行业通常期望的性能阈值内。如果不满足这些阈值,测试会失败,说明服务器不能舒服地处理指定负载。

const VUS = 100;

export const options = {
  scenarios: {
    load_3rules: {
      executor: "constant-vus",
      vus: VUS,
      duration: "60s",
    },
  },
  thresholds: {
    http_req_duration: ["p(99)<1000"],
    http_req_duration: ["p(95)<500"],
    http_req_duration: ["p(90)<200"],
    http_req_failed: ["rate<0.01"],
  },
};

export default () => {
  const user = http.get(URL_USER);
  const products = http.get(URL_PRODUCTS);
  sleep(1);
};

测试顺序

为了确保后端性能测试中的结果正确且一致,我会对 Appwrite 和 Supabase 按照完全相同的顺序执行。

start testing for instance
  enable clean CAX11
  install Appwrite
  install API client for Appwrite
  run the breakpoint test
  run the load test

  clean and clear the server

  install Supabase
  install the API client for Supabase
  run the breakpoint test
  run the load test
end testing for instance

一旦两个平台都在 CAX11 实例上完成测试,我会把服务器规格提高到 Hetzner CAX21 实例,并重复整个流程,比较在类似条件下,扩展服务器资源如何影响 Appwrite 和 Supabase 的性能。

假设

  • 服务器位于德国,而我距离它大约 1,500 km
  • 我使用 5G/4G LTE 移动网络连接服务器,模拟移动 app 更自然的流程,因为大多数终端用户会通过移动网络访问后端,而不是高速光纤连接
  • 用于负载测试的工具完全有能力执行这些测试。测试规模最多 10 000 VUs,完全在 k6 能力范围内,对这个工具来说不算巨大负载。

由于网络条件会变化,测试不是 100% deterministic,这就是现实世界测试的本质。网络波动等因素会引入响应时间的一些变化。不过,结果提供了这些平台在典型使用场景中表现的现实视角。这种方式确保我们捕捉到移动 app 与后端交互的自然流程,即使会发生一些轻微不一致。

结果

寻找 breakpoint

k6 会在测试后生成结果和图表(完整数据见 repository)。为了识别 breakpoint,我会关注请求失败率上升的区域(Point 1)。它与最长响应时间(Point 2)重合。根据标记的时间窗口,我再看 VUs 图表,确定失败点时有多少用户处于活跃状态(Point 3)。

这个点不意味着服务器已经完全冻结或停止工作。它仍然会返回响应,但它标志着首次 timeout 和关键错误开始出现的区域。

解释每秒请求数(req/s)时要小心。当更多用户发送请求,并且服务器及时响应时,这个值可能会上升。不过存在一个 tipping point:即使 VU 数量继续上升,服务器也无法及时处理所有内容。此时即便更多用户进来,请求速率也可能下降,说明服务器正在被压垮。

你可能已经注意到,即使还没到 breakpoint,服务器响应也可能达到 15 秒。虽然这比完全失败好,但离良好用户体验仍然很远。所以我加入了带固定阈值的 load test。

寻找舒适区

为了识别服务器舒适区的最大 VUs,我会手动运行一个 constant VUs 测试。如果满足阈值,这个值就符合限制,我可以在终端输出中观察到。然后我会手动提高这个值,直到达到最大可接受限制。之后我会重新运行测试,如果结果确认,就识别出了最大 VUs。

CAX11 - 2vCPU 4GB RAM

CAX21 - 4vCPU 8GB RAM

在这个测试中,Supabase 的表现几乎和 CAX11 实例完全一样,所以我允许自己跳过一些测试。数值最大变化大约 10%。

CAX31 - 8vCPU 16GB RAM

CAX41 - 16vCPU 32GB RAM

Cloud - Free Tier

为了提高数据可靠性,我在与 Hetzner vCPUs 相同的位置创建了 cloud servers。


总结和对比

舒适区

舒适区指服务器在维持可接受性能时,可以同时处理的 VUs 数量,这由具体阈值定义。提醒一下,良好用户体验的阈值是:

  • 90% 的请求 在 200ms 内返回
  • 95% 的请求 在 500ms 内返回
  • 99% 的请求 在 1000ms 内返回
  • 99% 的响应 成功(HTTP status 200)

在对比中,Appwrite 在维持这个舒适区方面一直表现更好,可以处理更多 VUs,同时保持响应时间低于这些阈值。

例如,在 CAX31 实例上,Appwrite 可以舒服支持 250 VUs,并且响应时间落在限制内。相比之下,Supabase 很难维持类似性能,只在 45 VUs 左右达到它的舒适区。

这些结果显示 Appwrite 可以支持显著更多的同时请求,在这些阈值内更具可扩展性。Supabase 虽然仍然可用,但更早达到舒适区,尤其是在处理更重负载时。

Breakpoint

图表显示每个平台在处理同时 VUs 时达到极限的点。这个 breakpoint 是在服务器开始返回失败或 timeout 时识别出来的。

在测试中,Appwrite 一直把限制推得更高。例如,在 CAX31 实例上,Appwrite 在 6,800 VUs 时达到 breakpoint,并处理了 100,000 个请求,平均响应时间 3 秒。相比之下,Supabase 更早达到 breakpoint,在 3,000 VUs,请求数 59,000,平均响应时间 6 秒。

这些结果说明 Appwrite 可以在性能开始下降之前处理显著更大的负载,而 Supabase 更早碰到 breaking point。

性能

对比 Supabase 和 Appwrite 的性能时,很明显 Appwrite 一直更高效地处理更大负载。在测试中,Appwrite 展示了更好的可扩展性。相反,Supabase 在较高负载下暴露出限制,特别是在 breakpoint tests 中,它很难在超过某个阈值后保持稳定性能。

值得注意的是,CAX31 实例,一个 self-hosted 选项,在处理 VUs 和提供更快响应时间方面超过了两个 cloud setup。 这说明选择 self-hosted setup 可以比依赖 cloud 环境提供更好的性能和可扩展性,让它成为要求较高应用的更低成本高性能方案。这很重要,因为 self-hosted 选项没有带宽、存储或账户方面的限制。只要服务器效率足够,你可以做任何需要的事。

使用 self-hosted 的另一个优势是可以 组合 实例 做 load balancing。这种方式不只是通过把负载分发到多台服务器来提升性能,也提供了一个 低成本方案。通过利用 CAX31 处理重任务,把较轻或后台进程 offload 到 CAX11,你可以在不打爆预算的情况下获得更好的整体性能。这种组合提供了一种既能扩展又保持可负担的高效方式。

易用性

说到易用性,Supabase 和 Appwrite 都开箱提供很好的 SDK,使得把它们的功能集成到应用里很简单。两个平台也都有直观控制面板,可以轻松定制和管理后端服务,无论你处理的是数据库、认证还是其他功能。

不过,Supabase 在 self-hosted 环境中配置更有挑战,因为它需要更多 setup 和定制才能让所有东西顺利运行。另外,Supabase panel 中有几个选项在 self-hosted setup 中被禁用或限制,这相比 cloud 版本会限制灵活性。Appwrite 提供更顺滑的 self-hosted 体验,限制更少,因此对于优先考虑易用性的人来说是更直接的选择。

Idle consumption

作为对比,在 CAX11 实例 idle 时,Appwrite 使用大约 30% 的服务器资源,而 Supabase 持续消耗惊人的 200%。这太不现实了,我一开始都不敢相信,所以又在 cloud 中重新跑了测试,但结果类似,确认这就是它的工作方式。

总体来说,Appwrite 更轻、更快、更容易配置,也更容易管理。另一方面,Supabase 真的只有在你绝对需要某个具体功能(比如 SQL)时,才值得在 cloud 中使用。

数字

想象你有 每月 50,000 个 eCommerce 用户。

  1. 假设用户主要在每月 25 天活跃,这给出每天 2,000 个用户。
  2. 假设大多数流量集中在每天 8 小时内,所以 2,000 / 8 = 每小时 250 个用户。
  3. 每小时 250 个用户大约是每分钟 4 个用户。为了考虑流量峰值,我们把它提高到 每分钟 20 个用户。

这意味着你可以用 CAX11 或 CAX21 实例管理基础设施,成本低至 每月 5 欧元,并提供 无限带宽 和存储大量文件的能力。

这些值基于 load tests,也说明服务器在达到 breaking point 前还能处理更多用户,不过随着负载上升,响应时间会变长。

记住,我用相当苛刻的场景跑测试,并且在 最便宜实例 上使用 Appwrite 时仍然一次达到超过 60 VUs。

给你的指南

  • 小 PoCs 从 Supabase 或 Appwrite cloud 开始,使用它们的 free tiers
  • 大多数情况选择 Appwrite,因为性能更好,也更轻量
  • 如果你需要 Supabase 提供的特定功能,选择 Supabase
  • Supabase 做 self-hosting 更难配置
  • 与 Appwrite 相比,Supabase idle 时消耗更多资源
  • 如果你知道以后会迁出 cloud,就避免 Supabase
  • 你很容易在性能更差的情况下用 AWS 超过 CAX41 的价格
  • 你可以为小查询选择小实例,为数据库选择强实例。自由组合它们
  • 你可以用开箱即用的免费工具获得很好的性能。启动项目不需要 AWS 上的 Docker Swarm

我几个小时生成的。

Source Code

你可以查看 source 来验证所有代码,并查看测试期间记录的详细报告,以图表形式展示。这些报告清楚可视化了服务器在不同负载下的性能。

https://github.com/codigee-devs/supabase-appwrite-performance-testing

Extra

我也在裸 Hetzner 服务器上跑了 breakpoint tests,以提供更深入的性能洞察。详细结果和更近距离的测试查看,请访问 repository。