sqlx-cli 迁移创建一个 todos 表,并通过一个小型 axum 服务器处理增删改查 (CRUD) 请求。所有连接均使用 TLS,并启用完整的证书验证。
前置条件
- Rust 1.94 或更高版本。本指南已在 Rust 1.98.1、
sqlx0.9.0、sqlx-cli0.9.0 和axum0.8.9 环境下测试通过。 - 一个 ClickHouse Cloud 账户
psql,用于创建数据库。您也可以在 SQL 控制台中运行CREATE DATABASE语句。
创建 ClickHouse Managed Postgres 服务
在 ClickHouse Cloud 控制台中,点击 New service,然后选择 Postgres。实例会在几分钟内就绪。详细操作步骤请参阅快速入门。获取连接详细信息
在服务的左侧边栏中点击 Connect。保持选中 Directly,并开启 Use SSL。此时,url 选项卡中的连接 URL 将以sslmode=verify-full&sslrootcert=<service-name>-ca-certificate.pem 结尾,端口为 5432。
点击 Download CA certificate。您也可以稍后在 Settings → CA Certificate 中下载。该证书为您的实例所独有,因此驱动程序可借此验证与之通信的确实是您的服务器。
在本指南中,应用和 sqlx-cli 均直接连接到端口 5432 上的 Postgres。axum 服务器是长期运行的进程,而 PgPool 本身就维护着连接池,因此无需在 Postgres 前再额外部署连接池器。迁移应始终使用直接连接。如果您运行多个应用实例,请参阅使用 PgBouncer。
设置项目
创建一个项目,添加sqlx (启用 Postgres 驱动程序、Tokio 运行时和 rustls TLS 后端) ,再添加 axum 以及几个辅助 crate:
sqlx-cli,并使用相同的 TLS 后端:
配置连接
将下载的 CA 证书移动到项目目录中,并重命名为ca-certificate.pem。
为应用创建一个数据库。将 <PASSWORD> 和主机替换为 Connect 对话框中显示的值:
.env 文件,或修改现有 .env 文件中的 DATABASE_URL,使其指向 guide_rust 数据库。应用程序和 sqlx-cli 都会从该文件读取 DATABASE_URL:
.env
sslmode 和 sslrootcert。使用 verify-full 时,它会检查服务器证书是否由您实例的 CA 签名,以及证书是否与主机名匹配。sslrootcert 路径相对于您执行命令时所在的目录进行解析。请勿将 .env 纳入版本控制;如果密码中包含 @、/ 或 # 等字符,请对其进行百分号编码。
通过迁移创建表
创建一个迁移文件:migrations 目录下新建的文件,添加以下表定义:
migrations/<timestamp>_create_todos.sql
sqlx migrate run 会将已应用的迁移记录在 _sqlx_migrations 表中,之后再次调用时只会运行新增的迁移。请将 migrations 目录随代码一起提交。如果希望改为在应用启动时执行迁移,可以在 main 中调用 sqlx::migrate!().run(&pool).await?,前提是该连接池使用的是直接连接。
构建 API
将src/main.rs 替换为一个小型 axum 服务器,每个路由执行一条查询。如果是在现有应用中,请按照 main 中的方式创建连接池,然后将 Todo 结构体、各个处理函数以及路由添加到你自己的路由器中:
src/main.rs
.bind 以参数 ($1) 的形式传入,绝不会直接拼接进 SQL 字符串。每条不同的 SQL 字符串在每个连接上只预编译一次,并存入可容纳 100 条语句的语句缓存;语句被逐出缓存时,sqlx 会将其关闭。
运行与验证
启动服务器:pg_stat_ssl,表明该会话已加密。在另一个终端中创建两个待办事项,将第一个标记为已完成,然后删除第二个:
-w '\n' 选项的作用只是在每个响应之后添加一个换行符:
http://localhost:3117/todos,即可查看剩余的待办事项列表:
guide_rust 和 public,然后点击 todos 表。旁边的 _sqlx_migrations 表记录了迁移历史。todos 表中保留的那一行如下:
使用 PgBouncer
如需让应用通过内置的 PgBouncer 进行连接,请在 Connect 对话框中选择 via PgBouncer,并在应用的DATABASE_URL 中使用端口 6432。CA 证书无需更换。例如,可以让 .env 继续使用直接连接以供 sqlx-cli 使用,并在启动应用时设置该变量:
pg_stat_ssl 报告的是 PgBouncer 自身到 Postgres 的连接,因此该检查会输出 none。但应用到 PgBouncer 的连接仍会经过验证:如果 CA 证书不正确,同样会报 UnknownIssuer 错误。
PgBouncer 以事务池化模式运行,因此每个事务都可能在不同的 Postgres 连接上执行。使用 sqlx 时需注意:
- 建立连接时需要设置
.extra_float_digits(None),参见main.rs。默认情况下,sqlx 会将extra_float_digits作为启动参数发送,而 PgBouncer 会拒绝该连接并报错unsupported startup parameter: extra_float_digits。出于同样的原因,PgConnectOptions::options也会报错unsupported startup parameter in options。 - 预处理语句在默认设置下即可正常使用。sqlx 从缓存中逐出语句时,会通过协议层的
Close消息将其释放,PgBouncer 能够处理该消息,在事务内部也不例外。不要设置statement_cache_capacity(0):这样做既无必要,而且关闭缓存后,sqlx 仍会预处理命名语句,却永远不会关闭它们。 - 不要对事务之外运行的查询使用
.persistent(false)。否则 sqlx 会预处理一条未命名语句,并在第二次往返中执行,而 PgBouncer 可能会将这次请求发送到另一个 Postgres 连接。这会导致查询报错unnamed prepared statement does not exist,或者悄无声息地执行其他客户端的语句并返回错误结果。 - 通过
SET进行的会话设置 (包括在连接池的after_connecthook 中进行的设置) 不会保留在你的连接上,还可能泄漏给其他客户端。请改为在事务中使用SET LOCAL。 - 迁移必须使用直接连接。
sqlx-cli和query!宏无法通过 PgBouncer 连接,因为它们总会发送extra_float_digits。sqlx::migrate!会获取一个会话级咨询锁 (pg_advisory_lock) ,而经由 PgBouncer 时,解锁请求可能被发送到另一个 Postgres 连接。这样一来,该锁会一直被池中的某个连接持有,后续的迁移 (包括通过直接连接执行的迁移) 都会挂起,直到 PgBouncer 关闭该连接为止。