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 与任何其他关系型数据库一样。你仍然可以
- 使用 Prisma Schema 语言 对你的数据库进行建模
- 在你的 schema 中使用 Prisma ORM 现有的
mysql
数据库连接器,以及 PlanetScale 提供给你的连接字符串 - 如果你在 PlanetScale 中已经有数据库 schema,可以使用 内省 功能用于现有项目
- 使用
db push
将你的 schema 更改推送到数据库 - 在你的应用程序中使用 Prisma Client 与 PlanetScale 的数据库服务器通信
需要考虑的差异
PlanetScale 的分支模型和可伸缩性设计意味着还需要考虑一些差异。在决定将 PlanetScale 与 Prisma ORM 结合使用时,你应该注意以下几点
-
分支和部署请求。PlanetScale 提供两种类型的数据库分支:开发分支,允许你测试 schema 更改;以及生产分支,受到直接 schema 更改的保护。相反,更改必须首先在开发分支上创建,然后使用部署请求部署到生产环境。生产分支具有高可用性,并包括每日自动备份。要了解更多信息,请参阅 如何使用分支和部署请求。
-
引用操作和完整性。为了支持跨多个数据库服务器的扩展,PlanetScale 默认不使用外键约束,这些约束通常用于关系型数据库中以强制执行不同表之间的数据关系,并要求用户在他们的应用程序中手动处理。但是,你可以通过 在 PlanetScale 数据库设置中显式启用它们。如果你没有显式启用它们,你仍然可以在数据中维护这些关系,并允许使用 引用操作,方法是利用 Prisma ORM 的能力,在
prisma
关系模式下 在 Prisma Client 中模拟关系。有关更多信息,请参阅 如何在 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 时,你需要为你的分支使用正确的连接字符串。给定数据库分支的连接 URL 可以在你的 PlanetScale 账户中找到,方法是进入分支的概览页面并选择“连接”下拉菜单。在“密码”部分,生成一个新密码并从下拉菜单中选择“Prisma”以获取 Prisma 格式的连接 URL。有关如何连接到 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"
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
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
字段上定义一个索引
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 及更高版本中,如果你的关系标量字段上没有索引,Prisma ORM 会发出警告。有关更多信息,请参阅 索引验证。
选项 2:在 PlanetScale 数据库设置中启用外键约束
PlanetScale 数据库对外键约束的支持已于 2024 年 2 月全面可用。请按照 PlanetScale 文档 中的说明在你的数据库中启用它们。
然后,你就可以使用 Prisma ORM 并在你的 Prisma schema 中定义关系,而无需额外的配置。
在这种情况下,你可以像使用其他支持外键约束的数据库一样定义关系,例如
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
文件中
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 缺少 User
和 Post
模型之间的关系
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?
}
在这种情况下,你需要手动添加关系
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 入门指南。
如何在你的 Prisma schema 中定义分片键(预览)
分片 是一种在数据库负载增长时进行扩展的流行技术。
截至 v6.10.0,Prisma ORM 原生支持 PlanetScale 上的分片(作为一项 预览 功能),通过 Prisma schema 中的 @shardKey
和 @@shardKey
属性,你可以将其应用于模型中应作为数据库设置中分片键的字段。
为了使用分片键属性,你需要在 generator
上指定 shardKeys
预览功能
generator client {
provider = "prisma-client-js"
output = "../generated/prisma"
previewFeatures = ["shardKeys"]
}
现在你可以使用 @shardKey
和 @@shardKey
属性了
单列分片键
model User {
id String @default(uuid())
region String @shardKey
}
多列分片键
model User {
id String @default(uuid())
country String
customerId String
@@shardKey([country, customerId])
}
如何在 Prisma ORM 中使用 PlanetScale 无服务器驱动(预览)
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
请确保将连接字符串中的主机值更新为 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
当使用 Node.js 18 以下的版本时,你必须提供一个自定义的 fetch 函数实现。我们推荐使用 undici
包,Node.js 内置的 fetch 函数就是基于它。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 结合使用时的最佳实践的更多提示,请观看我们的视频