前置条件
- uv 以及 Python 3.11 或更高版本。本指南已在 Python 3.13、FastAPI 0.142、SQLAlchemy 2.1、asyncpg 0.31、Alembic 1.20 和 Uvicorn 0.54 环境下测试通过。
- 一个 ClickHouse Cloud 账户
psql,用于创建数据库。也可以在 SQL 控制台中运行CREATE DATABASE语句。
创建 ClickHouse Managed Postgres 服务
在 ClickHouse Cloud 控制台中,点击 New service,然后选择 Postgres。几分钟后实例即可就绪。详细操作步骤请参阅快速入门。获取连接详细信息
在服务的左侧边栏中点击 Connect。弹出的对话框会显示您的用户名、密码、服务器和端口,并提供 Directly / via PgBouncer 切换开关。 本指南会同时用到这两种连接:- PgBouncer (端口
6432) :供应用使用。SQLAlchemy 会在每个 Uvicorn 工作线程中维护一个连接池,而内置的 PgBouncer 可让众多工作线程和副本共享少量 Postgres 连接。它以事务池化模式运行。 - 直连 (端口
5432) :供 Alembic 使用。迁移需要执行 DDL,可能耗时较长,还可能依赖会话状态,因此应直接连接 Postgres。
sslmode=verify-full&sslrootcert=... 结尾。asyncpg 不会读取这些参数,因此只需从对话框中复制密码和主机即可。CA 证书则由应用在代码中传入 (参见配置连接) 。
设置项目
创建一个项目,然后安装 FastAPI、Uvicorn、带有 asyncio extra 的 SQLAlchemy、asyncpg 和 Alembic:配置连接
将下载的 CA 证书移动到项目目录中,并重命名为ca-certificate.pem。
为应用创建一个数据库。将 <PASSWORD> 和主机地址替换为 Connect 弹窗中显示的值:
.env 文件,或将这些变量添加到现有的 .env 文件中。这两个 URL 仅端口不同,均使用 postgresql+asyncpg 方案 (scheme) ,且不带任何 SSL 参数:
.env
DATABASE_CA_CERT 的路径相对于执行命令时所在的目录进行解析。如果密码中包含 @、/ 或 # 等特殊字符,请对其进行 URL 编码。请勿将 .env 纳入版本控制。
创建 app/db.py。该文件负责构建 TLS 设置、引擎,以及供应用其他部分共用的会话工厂:
app/db.py
PROTOCOL_TLS_CLIENT 创建的 ssl.SSLContext 要求证书有效,并会校验主机名,效果等同于 sslmode=verify-full。SQLAlchemy 会通过 connect_args 将其传递给 asyncpg。
如果您希望将 TLS 设置保留在 URL 中,或者您的应用使用同步 SQLAlchemy 会话,请改用 psycopg 驱动程序来替代 asyncpg。它基于 libpq 构建,可直接使用 Connect 弹窗中的参数,例如
postgresql+psycopg://...:5432/guide_fastapi?sslmode=verify-full&sslrootcert=ca-certificate.pem,并且同时支持 create_engine 和 create_async_engine。定义模型
创建app/models.py,并在其中定义一个带类型注解的 SQLAlchemy 模型:
app/models.py
运行迁移
基于 async 模板创建 Alembic 环境:migrations/env.py 文件内容。新版本会加载你的模型以供 autogenerate 使用,并使用与应用相同的 TLS 设置直接连接 Postgres:
migrations/env.py
alembic.ini 中的占位值 sqlalchemy.url,因为 env.py 会自行创建引擎。
根据模型生成迁移脚本,然后执行迁移:
migrations/versions 目录中生成的文件,并将其与代码一同提交。Alembic 会在 alembic_version 表中记录已应用的修订版本。之后每次修改模型,都需要重新运行 revision --autogenerate 和 upgrade head。
添加 API 端点
创建app/main.py。每个请求都会通过 FastAPI 依赖项获取独立的 AsyncSession,应用关闭时,引擎的连接池也会随之关闭:
app/main.py
session.commit 时结束。会话关闭时,所有未提交的操作都会被回滚。执行 INSERT 时,SQLAlchemy 会通过 RETURNING 读回生成的 id 和 created_at,因此 create_todo 无需再额外执行一次查询。
运行与验证
启动应用:DELETE 请求返回 204 No Content。如果请求的 todo 不存在,则返回 404:
pg_stat_ssl:
guide_fastapi 和 public,然后点击 todos 表。旁边的 alembic_version 表保存着迁移历史记录。todos 表中只剩下一行数据:
按 Ctrl+C 停止应用。
使用 PgBouncer
使用默认的 asyncpg 和 SQLAlchemy 设置时,应用可以通过 PgBouncer 正常运行:- asyncpg 会将每个查询作为协议层的命名预处理语句执行,SQLAlchemy 则为每个连接最多缓存 100 条此类语句。内置的 PgBouncer 在事务池化模式下支持这些语句,而且 asyncpg 通过协议层的
Close消息 (而非 SQLDEALLOCATE) 释放它们。因此无需设置statement_cache_size=0,也无需自定义prepared_statement_name_func。如果您仍要关闭 asyncpg 的语句缓存,还需在 URL 中添加prepared_statement_cache_size=0,以同时关闭 SQLAlchemy 的缓存。 - 通过
SET设置的会话设置 (例如search_path) 不会延续到下一个事务,还可能泄漏给其他客户端。请改为在事务内使用SET LOCAL。 pg_stat_ssl反映的是 PgBouncer 与 Postgres 之间的连接,因此通过端口6432连接时,上文的 TLS 检查会返回(False, None)。但应用到 PgBouncer 的连接仍会经过验证:使用错误的 CA 证书时,连接同样会失败。- Alembic 不使用 advisory 锁,且每次
upgrade都在单个事务中执行,因此同样可以通过 PgBouncer 运行。不过对于耗时较长的迁移,直连仍是更好的选择。