跳到主要内容

PlanetScale

Prisma 和 PlanetScale 共同提供了一个开发环境,该环境利用 Prisma 的 ORM 和 PlanetScale 高度可扩展的基于 MySQL 的平台,优化了数据访问应用程序的快速、类型安全的开发。

本文档讨论了使用 Prisma ORM 和 PlanetScale 背后的概念,解释了 PlanetScale 与其他数据库提供商之间的共同点和差异,并引导你完成配置应用程序以与 PlanetScale 集成的过程。

什么是 PlanetScale?

PlanetScale 使用 Vitess 数据库集群系统,提供了一个与 MySQL 兼容的数据库平台。其特性包括:

  • 企业级可扩展性。 PlanetScale 提供了一个高可用性的生产数据库集群,支持跨多个数据库服务器进行扩展。这在无服务器环境中特别有用,因为它避免了 管理连接限制 的问题。
  • 数据库分支。 PlanetScale 允许你创建 数据库 schema 的分支,以便在将更改应用到生产数据库之前在开发分支上进行测试。
  • 支持 非阻塞 schema 更改 PlanetScale 提供了一种工作流,允许用户在不锁定数据库或导致停机的情况下更新数据库 schema。

与其他数据库提供商的共同点

使用 Prisma ORM 与 PlanetScale 的许多方面与使用 Prisma ORM 与任何其他关系型数据库一样。你仍然可以:

需要考虑的差异

PlanetScale 的分支模型和可扩展性设计意味着还有一些需要考虑的差异。在使用 Prisma ORM 与 PlanetScale 时,你应该注意以下几点:

  • 分支和部署请求。 PlanetScale 提供两种类型的数据库分支:开发分支,允许你测试 schema 更改;生产分支,防止直接进行 schema 更改。相反,更改必须首先在开发分支上创建,然后使用部署请求部署到生产环境。生产分支具有高可用性,并包含自动每日备份。要了解更多信息,请参阅 如何使用分支和部署请求

  • 引用操作和完整性。 为了支持跨多个数据库服务器进行扩展,PlanetScale 默认不使用外键约束,而外键约束通常用于关系型数据库中以强制执行不同表之间的数据关系,并要求用户在应用程序中手动处理此问题。但是,你可以在 PlanetScale 数据库设置中显式启用它们。如果你没有显式启用这些约束,你仍然可以在数据中维护这些关系,并通过使用 Prisma ORM 的能力在 Prisma Client 中使用 prisma 关系模式来模拟关系,从而允许使用 引用操作。更多信息,请参阅 如何在 Prisma Client 中模拟关系

  • 为外键创建索引。 当你在 Prisma ORM 中模拟关系 时(即在数据库层面不使用外键约束时),你需要为外键创建专门的索引。在标准的 MySQL 数据库中,如果表中的列具有外键约束,则会自动在该列上创建索引。当 PlanetScale 配置为不使用外键约束时,在 Prisma Client 模拟关系时,这些索引目前不会创建,这可能导致查询优化不佳的问题。为避免此问题,你可以在 Prisma ORM 中创建索引。更多信息,请参阅 如何为外键创建索引

  • 使用 db push 进行 schema 更改。 当你将开发分支合并到生产分支时,PlanetScale 将自动比较两个 schema 并生成自己的 schema 差异。这意味着 Prisma ORM 的 prisma migrate 工作流(它生成自己的迁移文件历史记录)在使用 PlanetScale 时并不自然契合。这些迁移文件可能无法反映 PlanetScale 合并分支时实际运行的 schema 更改。

    警告

    我们建议在使用 PlanetScale 进行 schema 更改时不要使用 prisma migrate。相反,我们建议你使用 prisma db push 命令。

    有关此工作原理的示例,请参阅 如何使用 db push 进行 schema 更改

  • 内省。当你对现有数据库进行内省,并且没有在 PlanetScale 数据库中启用外键约束 时,你将得到一个没有关系的 schema,因为关系通常是基于连接表的外键定义的。在这种情况下,你需要手动添加缺失的关系。更多信息,请参阅 内省后如何添加缺失的关系

如何使用分支和部署请求

使用 Prisma ORM 连接到 PlanetScale 时,你需要使用分支的正确连接字符串。可以通过访问分支的概览页面并选择“连接”下拉菜单,从你的 PlanetScale 账户中找到给定数据库分支的连接 URL。在“密码”部分,生成新密码并从下拉菜单中选择“Prisma”,以获取连接 URL 的 Prisma 格式。有关如何连接到 PlanetScale 数据库的更多详细信息,请参阅 Prisma ORM 的 入门指南

每个 PlanetScale 数据库都创建了一个名为 main 的分支,它最初是一个开发分支,你可以用来测试 schema 更改。一旦你对所做的更改感到满意,就可以 将其提升 为生产分支。请注意,你只能将新的更改推送到开发分支,因此进一步的更改需要在单独的开发分支上创建,然后稍后使用 部署请求 部署到生产环境。

如果你尝试推送到生产分支,你将收到 错误消息 Direct execution of DDL (Data Definition Language) SQL statements is disabled on this database.

如何使用关系(并启用引用完整性)与 PlanetScale

选项 1:在 Prisma Client 中模拟关系

1. 设置 relationMode = "prisma"

PlanetScale 默认不在其数据库 schema 中使用外键约束。但是,Prisma ORM 依赖底层数据库中的外键约束来强制执行 Prisma schema 中模型之间的引用完整性。

在 Prisma ORM 3.1.1 及更高版本中,你可以在 Prisma Client 中使用 prisma 关系模式模拟关系,这样就不需要在数据库中使用外键约束了。

要在 Prisma Client 中启用关系模拟,请在 datasource 块中将 relationMode 字段设置为 "prisma"

schema.prisma
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
relationMode = "prisma"
}
信息

设置关系模式的功能作为 referentialIntegrity 预览功能的一部分在 Prisma ORM 3.1.1 版本中引入,并在 Prisma ORM 4.8.0 及更高版本中普遍可用。

relationMode 字段在 Prisma ORM 4.5.0 版本中重命名,之前名为 referentialIntegrity

如果你在 Prisma schema 中使用关系,并且 relationMode 字段使用默认的 "foreignKeys" 选项,PlanetScale 将会报错,并且当 Prisma ORM 尝试创建外键时,会输出 P3021 错误消息。(在 2.27.0 版本之前,它会输出原始数据库错误。)

2. 为外键创建索引

当你在 Prisma Client 中模拟关系 时,你需要创建自己的索引。举一个需要添加索引的情况为例,请看下面这个包含帖子和评论的博客 schema:

schema.prisma
model Post {
id Int @id @default(autoincrement())
title String
content String
likes Int @default(0)
comments Comment[]
}

model Comment {
id Int @id @default(autoincrement())
comment String
postId Int
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
}

Comment 模型中的 postId 字段引用了 Post 模型中相应的 id 字段。然而,这在 PlanetScale 中并未实现为外键,因此该列没有自动索引。这意味着某些查询可能没有得到很好的优化。例如,如果你查询某个特定帖子 id 的所有评论,PlanetScale 可能需要进行全表查找。这可能会很慢,而且也很昂贵,因为 PlanetScale 的计费模式会根据读取的行数收费。

为了避免这种情况,你可以使用 Prisma ORM 的 @@index 参数postId 字段上定义索引

schema.prisma
model Post {
id Int @id @default(autoincrement())
title String
content String
likes Int @default(0)
comments Comment[]
}

model Comment {
id Int @id @default(autoincrement())
comment String
postId Int
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)

@@index([postId])
}

然后,你可以使用 db push 将此更改添加到你的 schema 中。

在 4.7.0 及更高版本中,如果你的关系 scalar 字段上没有索引,Prisma ORM 会发出警告。更多信息,请参阅 索引验证

警告

需要注意的一个问题是,隐式多对多关系 无法通过这种方式添加索引。如果查询速度或成本是个问题,在这种情况下,你可能需要使用 显式多对多关系

选项 2:在 PlanetScale 数据库设置中启用外键约束

PlanetScale 数据库中对外键约束的支持自 2024 年 2 月起已普遍可用。请按照 PlanetScale 文档 中的说明在你的数据库中启用它们。

然后,你就可以使用 Prisma ORM 并在 Prisma schema 中定义关系,而无需额外配置。

在这种情况下,你可以像支持外键约束的其他数据库一样定义关系,例如

schema.prisma
model Post {
id Int @id @default(autoincrement())
title String
content String
likes Int @default(0)
comments Comment[]
}

model Comment {
id Int @id @default(autoincrement())
comment String
postId Int
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
}

使用这种方法,无需进行以下操作:

  • 在 Prisma schema 中设置 relationMode = "prisma"
  • 为外键定义额外的索引

此外,内省将自动在你的 Prisma schema 中创建关系字段,因为它可以检测数据库中的外键约束。

如何使用 db push 进行 schema 更改

要将 db push 与 PlanetScale 一起使用,你需要首先在 Prisma Client 中启用关系模拟。在未启用引用模拟的情况下推送到分支,将导致 错误消息 Foreign keys cannot be created on this database.

举例来说,假设你决定在上面的博客文章 schema 中添加一个新的 excerpt 字段。你首先需要 创建一个新的开发分支并连接到它

接下来,将以下内容添加到你的 schema.prisma 文件中

schema.prisma
model Post {
id Int @id @default(autoincrement())
title String
content String
excerpt String?
likes Int @default(0)
comments Comment[]
}

model Comment {
id Int @id @default(autoincrement())
comment String
postId Int
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)

@@index([postId])
}

要推送这些更改,请在终端中导航到项目目录并运行

npx prisma db push

一旦你对开发分支上的更改满意,你可以打开部署请求将其部署到你的生产分支。

更多示例,请参阅 PlanetScale 关于使用 db push 通过 Prisma ORM 进行自动迁移 的教程。

内省后如何添加缺失的关系

注意:本节仅适用于使用 relationMode = "prisma" 通过 Prisma ORM 模拟外键约束的情况。如果你在 PlanetScale 数据库中启用了外键约束,则可以忽略本节。

使用 npx prisma db pull 进行内省后,你获得的 schema 可能缺少一些关系。例如,以下 schema 缺少 UserPost 模型之间的关系

schema.prisma
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
title String @db.VarChar(255)
content String?
authorId Int

@@index([authorId])
}

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}

在这种情况下,你需要手动添加关系

schema.prisma
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
title String @db.VarChar(255)
content String?
author User @relation(fields: [authorId], references: [id])
authorId Int

@@index([authorId])
}

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}

有关更详细的示例,请参阅 PlanetScale 的入门指南

如何将 PlanetScale 无服务器驱动与 Prisma ORM 一起使用(预览)

PlanetScale 无服务器驱动 提供了一种通过 HTTP 与数据库通信并执行查询的方法。

你可以使用 Prisma ORM 以及 PlanetScale 无服务器驱动,通过 @prisma/adapter-planetscale 驱动适配器进行连接。该驱动适配器允许你通过 HTTP 与数据库通信。

信息

此功能在 Prisma ORM 5.4.2 及更高版本中提供预览版。

要开始使用,请启用 driverAdapters 预览功能标志

generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}

生成 Prisma Client

npx prisma generate
信息

确保将连接字符串中的 host 值更新为 aws.connect.psdb.cloud。你可以在这里了解更多信息。

DATABASE_URL='mysql://johndoe:strongpassword@aws.connect.psdb.cloud/clear_nightsky?sslaccept=strict'

安装适用于 PlanetScale 的 Prisma ORM 适配器、PlanetScale 无服务器驱动和 undici

npm install @prisma/adapter-planetscale undici
信息

当使用低于 18 的 Node.js 版本时,你必须提供自定义的 fetch 函数实现。我们推荐使用 Node 内置 fetch 所基于的 undici 包。Node.js 18 及更高版本包含内置的全局 fetch 函数,因此你无需安装额外的包。

更新你的 Prisma Client 实例以使用 PlanetScale 无服务器驱动

import { PrismaPlanetScale } from '@prisma/adapter-planetscale'
import { PrismaClient } from '@prisma/client'
import dotenv from 'dotenv'
import { fetch as undiciFetch } from 'undici'

dotenv.config()
const connectionString = `${process.env.DATABASE_URL}`

const adapter = new PrismaPlanetScale({ url: connectionString, fetch: undiciFetch })
const prisma = new PrismaClient({ adapter })

然后,你就可以像往常一样使用 Prisma Client,并具有完整的类型安全。Prisma Migrate、内省和 Prisma Studio 将继续使用 Prisma schema 中定义的连接字符串正常工作。

更多关于使用 PlanetScale 与 Prisma ORM 的信息

开始使用 PlanetScale 与 Prisma ORM 的最快方式是参考我们的入门文档

这些教程将引导你完成连接到 PlanetScale、推送 schema 更改以及使用 Prisma Client 的过程。

有关同时使用 Prisma ORM 和 PlanetScale 的最佳实践的更多提示,请观看我们的视频