2. 什么是 Fauna?

Fauna 是一个多协议、多模型、多租户的分布式事务型数据库即服务(DBaaS)。听起来有点复杂?我们拆开看。

2.1. 数据库即服务(DBaaS)

简单粗暴地说,数据库即服务就是云厂商帮你托管数据库。你不用操心服务器维护、备份这些破事,只需要专注于业务逻辑——比如设计集合、索引、查询等。这样既保留了数据库的强大功能,又省去了运维的麻烦。

2.2. 分布式事务型数据库

分布式意味着数据库跑在多台服务器上。这带来两个好处:性能更高,容错性更强。一台服务器挂了,整个数据库照样跑。

事务型则保证数据操作的可靠性。事务内的操作要么全部成功,要么全部失败,不会出现数据半成品状态。

更关键的是,Fauna 提供了隔离级别,确保跨多个分布式节点的事务执行结果永远正确。这对分布式数据库至关重要——否则不同节点可能产生不同结果,导致数据不一致。

举个例子,两个事务操作同一条记录:

  1. 把值设为 "15"
  2. 值加 "3"

按顺序执行结果是 "18",反过来执行结果是 "15"。如果系统不同节点执行顺序不一致,数据就乱套了。

2.3. 多模型数据库

多模型数据库允许用不同方式存储各类数据,而且都在同一个数据库引擎里,用同一种连接访问。

Fauna 底层是文档数据库,每条记录都是 JSON 格式的结构化文档。这让它能:

  • 当键值存储用(文档只有一个 value 字段)
  • 当表存储用(文档有多个扁平字段)
  • 存储复杂结构(嵌套字段、数组等)
// 键值文档
{
  "value": "Baeldung"
}

// 表格式文档
{
  "name": "Baeldung",
  "url": "https://www.baeldung.com/"
}

// 结构化文档
{
  "name": "Baeldung",
  "sites": [
    {
      "id": "cs",
      "name": "Computer Science",
      "url": "https://www.baeldung.com/cs"
    },
    {
      "id": "linux",
      "name": "Linux",
      "url": "https://www.baeldung.com/linux"
    },
    {
      "id": "scala",
      "name": "Scala",
      "url": "https://www.baeldung.com/scala"
    },
    {
      "id": "kotlin",
      "name": "Kotlin",
      "url": "https://www.baeldung.com/kotlin"
    },
  ]
}

此外,Fauna 还支持:

  • 关系数据库特性:创建索引优化查询,跨集合约束保证数据一致性
  • 图查询:构建跨集合的复杂数据结构,像操作单个图一样访问
  • 时间模型:随时回溯数据库历史状态,查看记录变更历史或直接访问历史版本数据

2.4. 多租户数据库

多租户数据库支持多个用户共享同一个数据库服务。云托管数据库常见这种设计,一台服务器能服务多个客户。

但 Fauna 的实现有点特别:它用租户区分同一客户的不同数据子集。核心特性是支持数据库嵌套——可以创建子数据库,并为子数据库生成访问凭证。

关键点:

  • 只能访问当前连接数据库的子数据库(只读)
  • 不能访问父级或同级数据库

这种设计特别适合微服务架构:在父数据库下为每个服务创建子数据库,管理员可以跨所有子数据库查询(比如做数据分析时特别方便)。

2.5. 多协议数据库

提供多种方式访问同一份数据

标准方式是使用 Fauna 查询语言(FQL)通过官方驱动访问,能发挥数据库全部能力。

另外还提供 GraphQL 接口:

  • 优势:无需专用驱动,任何语言都能用 HTTP 客户端访问
  • 限制:需要预先定义 GraphQL Schema,无法支持同一集合中不同结构的记录

3. 创建 Fauna 数据库

知道 Fauna 能做什么了,现在动手创建一个。

没有账号先注册。登录后仪表盘点击 "Create Database":

fauna create db

然后设置数据库名称和区域组(Region Group):

  • 区域组影响费用(超出免费额度后)和外部访问的接口地址
  • 可选预置示例数据,帮助快速上手

fauna db region

创建完成后,数据库就立即可用:

  • 选了示例数据:包含预填充的集合、索引、自定义函数和 GraphQL Schema
  • 没选示例数据:空数据库,等待你创建结构

fauna db structure

最后需要创建密钥用于外部连接。在侧边栏 "Security" 标签页操作:

fauna auth key

注意:创建后立即复制密钥!离开页面后就无法再查看(安全设计)。

4. 与 Fauna 交互

数据库就绪,开始交互。Fauna 提供三种方式:

  • Fauna Shell(Web UI 内命令行)
  • FQL 驱动(编程语言专用)
  • GraphQL API(通用 HTTP 接口)

4.1. Fauna Shell

在 Web UI 直接执行命令,支持两种模式:

  • 使用配置的密钥(效果等同于外部连接)
  • 特殊管理员连接

fauna shell

实用场景:快速探索数据、测试应用查询(避免反复部署)。

4.2. 使用 FQL 连接

**需要官方驱动,支持 Java/Scala 等语言。Java 驱动要求 JDK 11+**。

Maven 依赖配置:

<dependency>
    <groupId>com.faunadb</groupId>
    <artifactId>faunadb-java</artifactId>
    <version>4.2.0</version>
    <scope>compile</scope>
</dependency>

创建客户端连接:

FaunaClient client = FaunaClient.builder()
    .withEndpoint("https://db.us.fauna.com/")  // 根据区域组调整
    .withSecret("your-authorization-key-here")  // 替换为你的密钥
    .build();

关键特性

  • 客户端内置连接池,按需创建连接
  • 应用启动时创建一次,全局复用
  • 不同密钥需要不同客户端(比如访问同一父数据库下的多个子数据库)

执行查询示例:

client.query(
    language.Get(language.Ref(language.Collection("customers"), 101))
).get();

4.3. 使用 GraphQL 连接

通过 GraphQL API 访问,只需 HTTP 客户端,无需专用驱动。

前置条件:必须先创建 GraphQL Schema,定义数据结构及其与 Fauna 集合/索引/函数的映射。完成后,任何 GraphQL 客户端(甚至 RestTemplate)都能访问。

限制:只能操作数据,管理操作(如创建集合/索引)仍需 FQL 或 Web UI。

连接要点:

  • 接口地址:https://graphql.us.fauna.com/graphql(美国区域)
  • 认证:在 Authorization 头部添加 Bearer Token(即密钥)
  • 请求方式:POST 请求,Body 包含查询/变更语句及可选变量

5. 在 Spring 中使用 Fauna

理解了基本用法,现在整合到 Spring 应用。Fauna 没有原生 Spring 驱动,我们需要配置标准 Java 驱动为 Spring Bean。

5.1. 基础配置

先配置两个关键参数

  • 区域(fauna.region):用于推导 FQL 和 GraphQL 接口地址
  • 密钥(fauna.secret):数据库访问凭证

application.properties 添加:

fauna.region=us
fauna.secret=fnAExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  # 替换为实际密钥

为什么只配区域不配 URL? 避免两个接口地址配置不一致的坑。

5.2. FQL 客户端配置

FaunaClient 注册为 Spring Bean:

@Configuration
class FaunaClientConfiguration {
    @Value("https://db.${fauna.region}.fauna.com/")
    private String faunaUrl;

    @Value("${fauna.secret}")
    private String faunaSecret;

    @Bean
    FaunaClient getFaunaClient() throws MalformedURLException {
        return FaunaClient.builder()
            .withEndpoint(faunaUrl)
            .withSecret(faunaSecret)
            .build();
    }
}

使用方式

  • 直接注入 FaunaClient,类似使用 JdbcTemplate
  • 可封装成领域对象,提供更友好的 API

5.3. GraphQL 客户端配置

GraphQL 没有标准客户端,我们用 RestTemplate 封装(WebFlux 应用可用 WebClient):

@Component
public class GraphqlClient {
    @Value("https://graphql.${fauna.region}.fauna.com/graphql")
    private String faunaUrl;

    @Value("${fauna.secret}")
    private String faunaSecret;

    private RestTemplate restTemplate = new RestTemplate();

    public <T> T query(String query, Class<T> cls) {
        return query(query, Collections.emptyMap(), cls);
    }

    public <T, V> T query(String query, V variables, Class<T> cls) {
        var body = Map.of("query", query, "variables", variables);

        var request = RequestEntity.post(faunaUrl)
            .header("Authorization", "Bearer " + faunaSecret)
            .body(body);
        var response = restTemplate.exchange(request, cls);

        return response.getBody();
    }
}

设计亮点

  • 两个重载方法:支持带/不带变量的查询
  • 泛型响应:自动反序列化到指定类型
  • 隔离底层 HTTP 细节:业务代码只需关注查询逻辑

6. 总结

本文快速介绍了 Fauna 数据库的核心特性:

  • 分布式事务:高可用 + 强一致性
  • 多模型支持:文档/关系/图/时间模型混合使用
  • 多租户设计:灵活的数据隔离方案
  • 多协议访问:FQL 和 GraphQL 双模式

还展示了在 Spring 中的集成方式:通过配置类将 Fauna 客户端注入 Spring 容器,无论是 FQL 还是 GraphQL 都能丝滑使用。

下次新项目不妨试试 Fauna?这些特性说不定能解决你头疼的分布式数据问题。


原始标题:Introduction to FaunaDB with Spring