发布于 2026-01-06 1 阅读
0

FaunaDB 中的数据建模入门

FaunaDB 中的数据建模入门

项目启动之初,需要做出大量的技术决策。其中一些决策可能对最终结果影响甚微,甚至毫无影响,纯粹是个人偏好(稍后会详细解释为何这一点仍然重要),而另一些则需要格外谨慎。数据持久化和管理便是其中之一。仓促构建的数据层可能会严重影响整个代码库。虽然许多影响是间接的而非直接的,但简而言之,开发人员完成任务所需的时间取决于初始技术栈的设计是否足够直观易用。

根据您选择的数据库的不同,初始数据建模时间也会有所不同,但FaunaDB平台在这方面表现出色,主要是因为它模糊了现有建模范式之间的界限。

例如,FaunaDB 的索引机制允许将索引用作主键(稍后会提供示例),从而实现精确匹配、范围查询、排序以及高效的视图渲染,使您可以快速访问数据子集,从而以计算换取存储空间。您甚至可以将来自多个集合的数据合并到一个索引中。一方面,您可以使用索引来防止信息过载,这类似于使用 GraphQL 的原因;另一方面,它类似于 SQL 视图和索引的组合,但 FaunaDB 的索引机制具有跨多台机器的可扩展性。

数据库和范式有很多种,每一种都有不同的方式来描述功能、管理和技术术语。

术语比较

数据库 FaunaDB MySQL MongoDB DynamoDB 火库
范例 无服务器云数据库 关系型数据库管理系统 NoSQL 文档存储 非关系型 NoSQL 数据库 NoSQL 文档云
容器 收藏 桌子 收藏 桌子 收藏
记录 文档 文档 物品 文档
查询 动物区系查询语言(FQL) 结构化查询语言(SQL) MongoDB 查询语言 (MQL) 基于控制台和 CLI 方法与听众

数据持久性的演变

自最早的数据库出现以来,各领域都发生了独特的变化。尽管如此,数据库领域似乎仍然是相对停滞不前、缺乏创新的领域,这也不无道理。人们仍然非常偏爱关系型数据库系统,将其视为值得信赖且经过充分测试的数据存储平台。这导致人们对传统数据库系统有着相当大的依赖,因此,我们将重点对比 SQL 和 FaunaDB,因为根据DB-Engines 的数据,目前大多数用户都在使用这些系统。

尽管它仍然是最流行的数据库范式,但这未必是最恰当的比较,因为无服务器云计算的出现已经彻底改变了格局,要了解 FaunaDB 中的数据建模有何不同,我们必须了解我们现在所处的位置。

NoSQL 数据库旨在解决关系型数据库无法满足现代开发工作流程的诸多问题。事实上,在比较针对分布式和可扩展性优化的数据存储时,不仅数据库结构上的差异,就连我们与数据交互的方式也变得模糊不清。FaunaDB 在 NoSQL 方法的基础上进行了改进,并通过使用熟悉的语法和术语最大限度地降低了入门门槛。文档以增强型 JSON 格式存储,因此支持所有基本数据类型,无需学习新术语。此外,它还添加了合理的特殊类型。

数据类型

基本类型 特殊类型
布尔值 字节
无效的 日期
数字
细绳 询问
文字 参考
大批
目的 时间戳

FaunaDB中的数据类型都是大家熟悉的领域。

FaunaDB 提供了对象嵌套深度的选项,您可以根据需要进行任意深度的嵌套,同时,它还支持数据规范化,使熟悉 SQL 的用户能够以关系型的方式构建数据。这只是 FaunaDB 将关系型数据库的优势与现代平台(重视可扩展性、多区域可用性和数据可塑性)相结合的众多方式之一。

GraphQL 的加入使 FaunaDB 数据库与 MongoDB Atlas 等新一代为现代应用程序添加状态的方案类似,甚至与 Hasura 类似,后者结合了 PostgreSQL 和 GraphQL API,提供关系型且易于查询的数据存储。然而,FaunaDB 的数据处理方式独树一帜,代表了数据库演进的新阶段(无服务器数据库),因为它专为提供低延迟访问、自动扩展和多区域分布而设计,同时又不牺牲数据一致性。这得益于 FaunaDB 所借鉴的 Calvin 协议,该协议允许分布式系统遵循 ACID 规范。FaunaDB 本身就能够高效地处理关系型、文档型和图模型。接下来,我们将通过一个建模示例场景来对比不同的处理过程。

UML 用于 FQL(动物群查询语言)

在构建关系数据库模型时,通常需要创建 UML 图。这是预先概念化数据的必要步骤。图中展示的是在线音乐平台上的数据库的一小部分。

关系型UML

UML 图通常与 MySQL 相关,但也适用于一般的关系数据库。

这是传统的数据设计方法。许多人将其与 SQL 联系起来,因为 SQL 在这种模型中应用广泛。FaunaDB 支持这种工作负载,但在扩展时更加灵活。类似的概念也存在,例如,如果我们确定每个文档都应该包含某个字段,则可以添加一个索引作为主键。在 FaunaDB 中,这可以在创建初始集合之后完成,而传统的、部署到生产环境的关系型数据库中,跨表更改主键则需要重构数据库。您可以参考 Fauna为 SQL 用户提供的 FQL速查表。

让我们使用 FQL 来搭建基本结构。

CreateCollection与 SQL 中的类似CREATE TABLE同样适用于CreateIndex

CreateCollection({name: "artists"});

CreateCollection({name: "songs"});

CreateCollection({name: "albums"});
Enter fullscreen mode Exit fullscreen mode

我们通过将 unique 设置为 true 来模拟每个表的主键。

CreateIndex({ 
  name: "artists_by_id", 
  source: Collection("artists"), 
  terms: [{ field: [ "data", "artist_id" ] }], 
  unique: true 
})

CreateIndex({ 
  name: "songs_by_id", 
  source: Collection("songs"), 
  terms: [{ field: [ "data", "song_id" ] }], 
  unique: true 
})

CreateIndex({ 
  name: "albums_by_id", 
  source: Collection("albums"), 
  terms: [{ field: [ "data", "album_id" ] }], 
  unique: true 
})
Enter fullscreen mode Exit fullscreen mode

值得注意的是,虽然建议预先对数据进行建模(即使是最基本的建模)以便更好地理解系统,但这并非绝对必要,因为 FaunaDB 的数据结构易于调整。此外,它还支持无限数量的子数据库,从而实现多租户,且不会造成数据库间的数据污染。例如,如果系统中的第二个唱片公司以不同的方式对旗下艺人进行分类,则可以创建两个子数据库。此外,这些唱片公司还可以创建其他独立运行的子数据库。在本示例中,我们将使用一层子数据库。这样,每个唱片公司都可以拥有一个门户来管理其所有艺人,而无需重新进行数据建模。上述查询可以直接在其中一个“唱片公司”子数据库中执行。子数据库的创建方式如下CreateDatabase({name: "record_label_1"})

动物区系数据结构

假设我们想添加一个“流派”字段,并将其与艺术家关联起来。一位音乐家的职业生涯可能会不断发展,因此会跨越多种流派。然而,同一种流派也可以分配给多位艺术家。这是 SQL 用户所熟知的“多对多”关系的一个常见例子。我们可以创建一个流派集合并填充数据。

CreateCollection({ name: "genres" });

Foreach( ["Hip-Hop", "Electronic", "Rock"], 
  Lambda("genre", Create(Collection("genres"), { data: { name: Var("genre") } })))
Enter fullscreen mode Exit fullscreen mode

然后,我们为该系列创建一个索引,以便我们可以使用类型名称对其进行搜索。

CreateIndex({ 
  name: "genre_by_name",
  source: Collection("genres"),
  terms: [{ field: ["data", "name"] }],
  unique: true 
})
Enter fullscreen mode Exit fullscreen mode

我们建立了艺术家 ID 为 1 与“摇滚”这一音乐流派之间的关联。两个索引的唯一性均已设置为 true,因此我们的数据准确无误。要建立反向关联,我们只需交换索引即可。

Create( 
  Collection("music_type"),
  { 
   data: {
     artist: Select("ref", Get(Match(Index("artists_by_id"), "1"))), 
     genre: Select("ref", Get(Match(Index("genre_by_name"), "Rock"))) 
    } 
  } 
)
Enter fullscreen mode Exit fullscreen mode

最后,要获取与某位艺术家相关的所有音乐流派列表,我们可以运行以下查询。请注意Lambda运行 FQL 代码的匿名函数。

Map(
  Paginate( Match( Index("music_type"), Select("ref", 
  Get(Match(Index("artists_by_id"), "1"))) ) ), 
  Lambda("genre",
    Select(["data", "genre"], Get(Var("genre"))) 
  ) 
)
Enter fullscreen mode Exit fullscreen mode

这个例子通常出现在图建模中,但在这里它被结合使用了关系模型和文档模型的最佳方面。

FaunaDB 图模型

开发者体验 (DX)

FaunaDB 以这种方式简化了数据建模流程,回归到数据存储的选择更多地取决于偏好或习惯而非我们通常的认知。数据库适应数据,而非反之。这缩短了数据建模流程,因为您可以根据需要随时定义各种键、索引甚至整个数据库。在设计传统表时,您可能会过度设计某些非关键数据,从而浪费开发时间。FaunaDB 的灵活性在于,它允许在创建后进行修改,而不会使建模流程的各个方面变得复杂,并且真正实现了无服务器运行。所有操作均按使用量计费,基于完全托管的基础架构,无需管理任何资源,也不会为闲置时间付费。此外,FaunaDB 数据管理器还提供了一个支持系统,用于迁移、备份和恢复数据。

开发者体验

需要记住的是,开发者同时也是用户。

由于 JAMStack 能够让开发者专注于构建产品的独特之处,因此它正受到广泛关注。FaunaDB 简化了数据建模的复杂性,并允许开发者在几分钟内构建最常用的模型,因此它是首个符合这一新技术栈的数据库,因为它在设计之初就采用了与市面上其他数据库截然不同的工作方式。这种灵活性带来的最有趣的方面之一是,开发者可以根据每个项目的需求创建自己的数据建模范式。一旦 FQL 被广泛接受,我们将看到更多使用现有数据库无法实现的数据建模方法。事实上,FaunaDB 提供的范式旨在对比相似之处,但在我使用该平台的过程中,我发现它需要一种全新的状态管理视角。这种视角应该像其他与之配合使用的工具一样易于使用,并且随着时间的推移而不断完善。(该公司成立至今只有 9 年,相比之下,SQL 的概念早在 1970 年就已出现。)

该系统支持上传 GraphQL schema 以自动生成数据库,并可根据数据演变灵活组合所需的模型。这使得系统进入了数据管理的新时代,并最终证明,对数据进行建模的最佳方式或许就是根本不建模。

文章来源:https://dev.to/b_bot/modelling-data-in-faunadb-a-primer-51d3