PlanetScale
Prisma 和 PlanetScale 共同提供了一个开发环境,使用 Prisma 的 ORM 和 PlanetScale 的高度可扩展的基于 MySQL 的平台,优化了数据访问应用程序的快速、类型安全开发。
本文档讨论了使用 Prisma ORM 和 PlanetScale 背后的概念,解释了 PlanetScale 与其他数据库提供商之间的共性和差异,并指导您完成配置应用程序以与 PlanetScale 集成。
什么是 PlanetScale?
PlanetScale 使用 Vitess 数据库集群系统来提供一个与 MySQL 兼容的数据库平台。其功能包括
- 企业级可扩展性。PlanetScale 提供一个高度可用的生产数据库集群,支持跨多个数据库服务器进行扩展。这在无服务器环境中特别有用,因为它避免了不得不 管理连接限制 的问题。
- 数据库分支。PlanetScale 允许您创建 数据库模式的分支,以便您可以在将更改应用到生产数据库之前在开发分支上测试更改。
- 支持 非阻塞模式更改。PlanetScale 提供了一个工作流程,允许用户更新数据库模式,而不会锁定数据库或造成停机。
与其他数据库提供商的共性
在将 Prisma ORM 与 PlanetScale 配合使用时,许多方面与将 Prisma ORM 与任何其他关系型数据库配合使用相同。您仍然可以
- 使用 Prisma Schema Language 对数据库进行建模
- 在您的模式中使用 Prisma ORM 现有的
mysql
数据库连接器,以及 PlanetScale 提供给您的 连接字符串 - 对于现有项目,如果您的数据库模式已存在于 PlanetScale 中,则可以使用 内省
- 使用
db push
将模式中的更改推送到数据库 - 在您的应用程序中使用 Prisma Client 与 PlanetScale 上的数据库服务器进行通信
需要考虑的差异
PlanetScale 的分支模型和可扩展性设计意味着还需要考虑一些差异。在决定将 PlanetScale 与 Prisma ORM 配合使用时,您应该注意以下几点
-
分支和部署请求。PlanetScale 提供两种类型的数据库分支:开发分支,允许您测试模式更改;生产分支,受保护,无法直接进行模式更改。相反,更改必须首先在开发分支上创建,然后使用部署请求部署到生产环境。生产分支具有高可用性,并包含自动每日备份。要了解更多信息,请参阅 如何使用分支和部署请求。
-
引用操作和完整性。为了支持跨多个数据库服务器进行扩展,PlanetScale 默认情况下不使用外键约束,外键约束通常在关系型数据库中用于强制执行不同表中数据之间的关系,并要求用户在他们的应用程序中手动处理此问题。但是,您可以在 PlanetScale 数据库设置中显式地 启用它们。如果您没有显式启用它们,您仍然可以在数据中维护这些关系,并允许使用 引用操作,方法是使用 Prisma ORM 的能力来 使用
prisma
关系模式在 Prisma Client 中模拟关系。有关更多信息,请参阅 如何在 Prisma Client 中模拟关系。 -
在外键上创建索引。在 Prisma ORM 中模拟关系(即,不在数据库级别使用外键约束)时,您需要在外键上创建专用索引。在标准 MySQL 数据库中,如果一个表有一个带有外键约束的列,则会在该列上自动创建一个索引。当 PlanetScale 被配置为不使用外键约束时,当 Prisma Client 模拟关系时,这些索引 目前 不会创建,这会导致查询未得到良好优化的问题。为了避免这种情况,您可以在 Prisma ORM 中创建索引。有关更多信息,请参阅 如何在外键上创建索引。
-
使用
db push
进行模式更改。当您将开发分支合并到生产分支时,PlanetScale 将自动比较这两个模式并生成它自己的模式差异。这意味着 Prisma ORM 的prisma migrate
工作流程(它生成自己的迁移文件历史记录)在与 PlanetScale 配合使用时并不适合。这些迁移文件可能无法反映 PlanetScale 在合并分支时运行的实际模式更改。警告我们建议在对 PlanetScale 进行模式更改时不要使用
prisma migrate
。相反,我们建议您使用prisma db push
命令。有关此工作原理的示例,请参阅 如何使用
db push
进行模式更改 -
内省。当您对现有数据库进行内省并且没有在 您的 PlanetScale 数据库中启用外键约束 时,您将获得一个没有关系的模式,因为它们通常是根据连接表的外部键定义的。在这种情况下,您需要手动添加缺失的关系。有关更多信息,请参阅 如何在内省后添加缺失的关系。
如何使用分支和部署请求
当使用 Prisma ORM 连接到 PlanetScale 时,您需要使用您的分支的正确连接字符串。您可以在 PlanetScale 帐户中找到给定数据库分支的连接 URL,方法是转到分支的概述页面并选择“连接”下拉菜单。在“密码”部分,生成一个新密码并从下拉菜单中选择“Prisma”以获取连接 URL 的 Prisma 格式。有关如何连接到 PlanetScale 数据库的更多详细信息,请参阅 Prisma ORM 的 入门指南。
每个 PlanetScale 数据库都创建了一个名为 main
的分支,它最初是一个开发分支,您可以使用它来测试架构更改。如果您对在那里所做的更改感到满意,您可以 将其提升 成为生产分支。请注意,您只能将新更改推送到开发分支,因此进一步的更改需要在单独的开发分支上创建,然后使用 部署请求 部署到生产环境。
如果您尝试推送到生产分支,您将收到 错误消息 Direct execution of DDL (Data Definition Language) SQL statements is disabled on this database.
。
如何在 PlanetScale 中使用关系(并启用引用完整性)
选项 1:在 Prisma Client 中模拟关系
1. 设置 relationMode = "prisma"
PlanetScale 默认情况下不会在数据库架构中使用外键约束。但是,Prisma ORM 依赖于底层数据库中的外键约束来强制执行 Prisma 架构中模型之间的引用完整性。
在 Prisma ORM 3.1.1 及更高版本中,您可以 使用 prisma
关系模式在 Prisma Client 中模拟关系,这避免了在数据库中需要外键约束。
要在 Prisma Client 中启用关系模拟,请在 datasource
块中将 relationMode
字段设置为 "prisma"
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
relationMode = "prisma"
}
设置关系模式的能力是在 Prisma ORM 3.1.1 版本中作为 referentialIntegrity
预览功能的一部分引入的,并且在 Prisma ORM 4.8.0 及更高版本中普遍可用。relationMode
字段在 Prisma ORM 4.5.0 版本中被重命名,以前称为 referentialIntegrity
。
如果您在 Prisma 架构中使用关系,并将 relationMode
字段的默认选项设置为 "foreignKeys"
,PlanetScale 会报错,并且当 Prisma ORM 尝试创建外键时,会输出 P3021 错误消息。(在 2.27.0 之前的版本中,它将输出原始数据库错误。)
2. 在外键上创建索引
当 您在 Prisma Client 中模拟关系 时,您需要创建自己的索引。以一个想要添加索引的情况为例,假设有一个包含帖子和评论的博客的架构
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
。
在 4.7.0 及更高版本中,如果您有一个关系没有在关系标量字段上进行索引,Prisma ORM 会向您发出警告。有关更多信息,请参阅 索引验证。
选项 2:在 PlanetScale 数据库设置中启用外键约束
自 2024 年 2 月起,PlanetScale 数据库中的外键约束支持已普遍可用。按照 PlanetScale 文档 中的说明在您的数据库中启用它们。
然后,您可以使用 Prisma ORM 并在 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 架构中设置
relationMode = "prisma"
- 在外键上定义额外的索引
此外,内省会自动在 Prisma 架构中创建关系字段,因为它可以检测数据库中的外键约束。
如何使用 db push
进行架构更改
要将 db push
与 PlanetScale 一起使用,您首先需要 在 Prisma Client 中启用关系模拟。在未启用引用模拟的情况下将更改推送到您的分支将给出 错误消息 Foreign keys cannot be created on this database.
。
例如,假设您决定将一个新的 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 关于 使用 Prisma ORM 进行自动迁移 的教程,方法是使用 db push
。
如何在内省后添加缺少的关系
注意:本节仅在您使用
relationMode = "prisma"
来使用 Prisma ORM 模拟外键约束时才相关。如果您在 PlanetScale 数据库中启用了外键约束,则可以忽略本节。
使用 npx prisma db pull
进行内省后,您得到的架构可能缺少某些关系。例如,以下架构缺少 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 ORM 中使用 PlanetScale 无服务器驱动程序(预览)
PlanetScale 无服务器驱动程序 提供了一种通过 HTTP 与您的数据库进行通信并执行查询的方法。
您可以使用 Prisma ORM 以及 @prisma/adapter-planetscale
驱动程序适配器来使用 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:[email protected]/clear_nightsky?sslaccept=strict'
安装 PlanetScale 的 Prisma ORM 适配器、PlanetScale 无服务器驱动程序和 undici
包
npm install @prisma/adapter-planetscale @planetscale/database undici
在使用低于 18 的 Node.js 版本时,您必须提供自定义 fetch 函数实现。我们建议使用 undici
包,Node 的内置 fetch 就是基于该包的。Node.js 18 及更高版本包含一个内置的全局 fetch
函数,因此您不必安装额外的包。
更新 Prisma Client 实例以使用 PlanetScale 无服务器驱动程序
import { Client } from '@planetscale/database'
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 client = new Client({ url: connectionString, fetch: undiciFetch })
const adapter = new PrismaPlanetScale(client)
const prisma = new PrismaClient({ adapter })
然后,您可以像往常一样使用 Prisma Client,并获得完整的类型安全性。Prisma Migrate、Introspection 和 Prisma Studio 将继续像以前一样使用 Prisma 架构中定义的连接字符串工作。
更多关于使用 PlanetScale 与 Prisma ORM
使用 PlanetScale 与 Prisma ORM 的最快方法是参考我们的入门文档。
这些教程将引导您完成连接到 PlanetScale、推送架构更改以及使用 Prisma Client 的过程。
有关在将 Prisma ORM 与 PlanetScale 一起使用时的最佳实践的更多提示,请观看我们的视频。