Astro DB:深入探索

作者
Matthew Phillips

昨天,我们发布了一个专为 Astro Web 框架设计的完全托管 SQL 数据库服务。让我们深入了解 Astro DB 的实现细节:它的工作原理,我们为什么构建它,以及我们为什么采用 libSQL。

我们是如何走到这一步的

Astro 的独特之处在于它专注于构建**内容驱动的网站**。这当然以内容为中心,这就是为什么我们在 Astro 2.0 中发布了内容集合(Content Collections)。我们的用户喜欢它作为管理本地内容的方式。

WordPress 一直是我们重要的灵感来源。WordPress 之所以如此特别,其中一点就是它**内置的数据库**。你不仅可以管理文章内容,还可以管理数据、页面、区块、图片以及整个插件生态系统。

我们希望 Astro 也能有类似的功能,但我们很快意识到静态仓库数据能实现的功能是有限的。

发现(和失去)SQLite

为了通过数据集合和引用来发展内容集合,我们意识到我们正在做的事情是从文件系统构建一个类似数据库的 ORM,并且在这样做时遇到了几个挑战。核心团队成员 Erika 提出了一个想法:为什么我们不直接使用数据库 SQLite 呢?

我们爱上了将轻量级数据库内置到 Astro 中的想法。SQLite 非常适合大多数内容驱动网站的读密集型工作负载。

我们对这个想法进行了原型设计,但遇到了一些障碍。SQLite 是一个 C 语言库,因此它需要在 Node.js 中运行原生插件。这对于本地开发来说没问题,但原生插件很难部署到无服务器主机,并且启动时间也令人担忧。此外,像 StackBlitz 这样的关键环境根本无法运行它。

我们感到沮丧,于是将这个想法搁置,转而专注于其他事情。那是 2023 年的春天。

发现(并爱上)libSQL

与此同时——在世界的另一边,我们完全不知情——另一个团队正在解决这个确切的问题。那个团队就是 Turso,他们的解决方案是 libSQL

libSQL 是 SQLite 的一个分支,它引入了一系列运行时改进,同时保持了与经典 SQLite 的兼容性。libSQL 为 JavaScript/TypeScript 提供了一个现代数据库客户端,避免了困扰生态系统中其他部分的本地绑定和编译步骤。它甚至可以通过 WASM 在 StackBlitz 上运行。

Turso 还提供 libSQL 数据库托管服务,特别关注我们所需的规模(稍后会详细介绍)。Astro DB 的愿景开始形成,但直到 2023 年 12 月,所有碎片才最终拼合到位。

以 Astro 的方式设计本地数据库

一旦你启动开发服务器,Astro DB 就会为你提供一个完全本地的 libSQL 数据库。鉴于我们作为静态站点生成器的背景,数据库能够在启动时从头开始构建非常重要,这样未来它就可以为内容层提供支持,内容层的数据可以从包括文件系统在内的各种来源获取。

当你运行 astro dev 时,Astro DB 将会:

  • .astro/data.db 创建一个空数据库
  • db/config.ts 读取你的模式
  • db/seed.ts 填充数据库
  • 你的数据库客户端已准备就绪

这个工作流程很大程度上借鉴了 Astro 用户已经喜欢的内容集合工作流程。重要的是,数据库本身(data.db)是非持久化的。每次你启动开发服务器时,它都会从头开始创建。这为你提供了简单、可重现的一次性数据库。

Astro DB 将 Web 框架、模式、种子文件和数据库本身整合到一个内聚的整体中。我们甚至为你提供了一个 ORM:Drizzle。我们选择 Drizzle 是因为它是一个类型安全的 ORM,让你能够尽可能地接近底层,而且它也是可插拔的,这使我们能够在其之上添加自己的行为。

使用托管数据库进行远程操作

Astro DB 包含一个托管的 libSQL 数据库,你可以在本地开发和生产环境中连接到它。所有内容都通过我们的 Astro Studio 平台为你管理。你可以在大约 30 秒内为你的项目创建一个新数据库。

为了实现我们知道在我们的平台上所需的规模,我们与 Turso 合作,他们维护 libSQL 并运营着最大的 libSQL 托管平台。他们对“每个租户一个数据库”模型的承诺,完美契合了我们按需启动数十万个数据库的需求。

去年,我们还花了一些时间对 Cloudflare 的 D1 产品进行了原型设计。我们喜欢该项目的愿景,但当我们只需要数据库时,却遇到了 workers 和 worker 绑定所带来的额外抽象层的问题。我们也犹豫在专有数据库技术(D1)上进行构建,特别是如果这意味着将该技术捆绑到 Astro 本身中。最终,我们发现 D1 不适合我们的用例。

零停机时间模式迁移

你的数据库模式在项目中的 db/config.ts 文件中定义。当你更改模式时,你需要以消除数据丢失和停机风险的方式将其推送到你的托管数据库。因此我们构建了 astro db push 命令。

push 命令旨在平衡易用性,同时仍鼓励在生产大规模项目中有效的最佳实践。在 Astro DB 中,没有迁移文件需要管理。相反,当你运行 push 时,你的模式会自动与你的托管生产数据库进行比较,并应用任何新的更改。如果这些更改无法安全添加或以任何方式存在数据丢失风险,则更改将不会被应用。

这鼓励了 Astro DB 应用程序中的“扩展和收缩”迁移策略。当然,如果你正在进行快速开发并且不介意根据需要重置数据库,你可以运行 astro db push --force-reset 来推送你想要的任何模式更改,包括数据库重置。

在确定这个最终版本之前,我们经历了几次不同的模式迁移系统迭代。曾几何时,我们甚至有一个 migrations/ 文件夹,你可以在其中手动创建并将明确的迁移计划文件检入你的仓库。虽然有些人喜欢这种模式,但我们发现对于大多数用户来说,每次更改模式时都要记住去创建迁移文件是一个令人烦恼的额外步骤。

总结

我们对 Astro DB 的首次迭代中找到的平衡感到满意,它为未来的本地用例奠定了基础,同时提供了一种轻松部署生产数据库的方式。本文中省略的一个细节是,集成如何也能够提供自己的表和数据,我们希望在继续构建 Astro 内容和插件的下一个迭代时,能够对此进行更多探索。

要开始集成你的应用程序,请查阅文档