pgxpool 则是它提供的连接池。在本指南中,您将通过经过证书验证的 TLS 连接,将 Go 服务接入 ClickHouse Managed Postgres,借助 golang-migrate 迁移创建 todos 表,并由 net/http 服务器处理增删改查 (创建、读取、更新和删除) 请求。
前置条件
- Go 1.25 或更高版本。本指南已在 Go 1.27.1、pgx 5.11.0 和 golang-migrate 4.20.1 环境下测试通过。
- 一个 ClickHouse Cloud 账户
psql,用于创建数据库。也可以在 SQL 控制台中运行CREATE DATABASE语句。
创建 ClickHouse Managed Postgres 服务
在 ClickHouse Cloud 控制台中,点击 New service,然后选择 Postgres。实例会在几分钟内就绪。详细操作步骤请参阅快速入门。获取连接信息
在服务的左侧边栏中点击 Connect。保持选中 Directly,并开启 Use SSL。此时 url 选项卡中的连接 URL 会使用端口5432,并以 sslmode=verify-full&sslrootcert=<service-name>-ca-certificate.pem 结尾。点击 Download CA certificate。您也可以稍后在 Settings → CA Certificate 中下载。该证书为您的实例所独有,因此 pgx 可以用它来验证所连接的确实是您的服务器。
本指南中,无论是执行迁移还是运行应用程序,都通过端口 5432 直接连接 Postgres。Go 服务是长期运行的进程,pgxpool 已在进程内维护了连接池,因此无需再额外使用连接池器。如果您运行了该服务的多个实例,且所需的客户端连接数超出 Postgres 的上限,请让应用程序改用内置的 PgBouncer (via PgBouncer,端口 6432) 。具体请参阅使用 PgBouncer,该方式需要额外配置一项连接设置。
搭建项目
创建一个 Go 模块,添加 pgx,并安装带 pgx 驱动程序的migrate 命令行客户端:
-tags 'pgx5' 标志会在编译 migrate 命令行客户端时启用其 pgx v5 驱动程序,该驱动程序使用 pgx5:// URL 协议。go install 会将二进制文件安装到 $GOBIN;如果未设置 GOBIN,则安装到 $(go env GOPATH)/bin,因此请确保该目录已加入 PATH。
配置连接
将下载的 CA 证书移至项目目录,并重命名为ca-certificate.pem。
为应用创建一个数据库。将 <PASSWORD> 和主机地址替换为 Connect 弹窗中显示的值:
guide_go 数据库,唯一的区别在于 URL 协议:应用程序读取 DATABASE_URL,而 migrate 命令行客户端使用 MIGRATE_URL:
libpq 相同。使用 sslmode=verify-full 时,pgx 会从 sslrootcert 加载 CA 文件,并同时校验证书链和主机名,因此无需编写任何 TLS 代码。sslrootcert 路径相对于运行命令时所在的目录进行解析。如果密码包含 @、/ 或 # 等特殊字符,请对其进行 URL 编码。
通过迁移创建 schema
创建迁移文件:up 迁移的内容:
migrations/000001_create_todos.up.sql
down 迁移:
migrations/000001_create_todos.down.sql
migrate 会在 schema_migrations 表中记录当前版本,之后再运行时只会应用更新的迁移。请将 migrations 目录与代码一同提交。
请始终通过直接连接 (端口
5432) 运行 migrate。它在运行期间会持有一个会话级咨询锁 (advisory lock) ,以防止两个部署同时执行迁移。如果经由 PgBouncer 连接,加锁和解锁可能会在不同的 Postgres 连接上执行,导致锁始终未被释放,之后再运行 migrate 时会挂起,或因 timeout: can't acquire database lock 报错而失败。查询数据库
创建db.go。该文件基于 DATABASE_URL 创建连接池,并在启动时向 Postgres 查询当前会话使用的 TLS 版本,以此检查连接是否正常:
db.go
todos.go,在其中编写 HTTP 处理程序。每个处理程序都会在连接池上执行一条参数化查询,再由 pgx.CollectRows 和 pgx.CollectExactlyOneRow 按列名将结果行扫描到 Todo 结构体中:
todos.go
$1) 传递值,而不是将其拼接到 SQL 字符串中,可以防止 SQL 注入。pgx 会将这些值与查询文本分开发送。您无需单独检查 pool.Query 返回的错误,因为 pgx.CollectRows 和 pgx.CollectExactlyOneRow 会返回该错误。
创建 main.go。该程序为整个进程创建一个连接池,在端口 3114 上提供上述路由,并在按下 Ctrl+C 时关闭连接池。如果是现有应用,则应在启动时调用一次 newPool,并在您的 http.ServeMux 上调用 registerTodoRoutes:
main.go
运行并验证
将pgxpool 的其余依赖项添加到 go.mod 和 go.sum 中,然后启动服务器:
TLSv1.3 取自服务器上的 pg_stat_ssl,由此可以确认该会话已加密。如果将 sslrootcert 指向其他任意证书,服务器将无法启动,并在日志中输出 x509: certificate signed by unknown authority,这说明证书确实经过了校验。
在另一个终端中,创建两个待办事项,将第一个标记为已完成,然后删除第二个:
PATCH 请求会返回更新后的行:
http://localhost:3114/todos,即可列出剩余的待办事项。pgx 返回的 timestamptz 值采用本地时区。响应如下所示:
Shutting down,并关闭连接池。
要在控制台中查看数据,请在服务的左侧边栏中打开 SQL 控制台,依次展开 guide_go 和 public,然后点击 todos 表。旁边的 schema_migrations 表记录了迁移版本。todos 表中仅剩下一行数据:
使用 PgBouncer
如需让应用的查询经由内置的 PgBouncer 发送,请在 Connect 弹窗中选择 via PgBouncer,将DATABASE_URL 中的端口改为 6432,并添加 default_query_exec_mode=cache_describe。MIGRATE_URL 仍使用端口 5432:
default_query_exec_mode,但不会将其发送到服务器。无需修改代码,原有的 CA 证书也照常可用。启动日志现在会显示 TLS: none,这是因为 pg_stat_ssl 报告的是 PgBouncer 自身到 Postgres 的连接。应用到 PgBouncer 的连接仍会经过验证:如果 CA 证书不正确,连接同样会失败。
PgBouncer 以事务池化模式运行,因此每个事务都可能在不同的 Postgres 连接上执行:
- 通过
pool.Begin或pgx.BeginFunc使用事务不受影响,可照常工作。 - 通过
SET进行的会话设置不会保留在您的连接上,还可能泄漏给其他客户端。请改为在事务内使用SET LOCAL。此外,如果连接 URL 中包含search_path,PgBouncer 会拒绝连接并报错unsupported startup parameter: search_path;请改用带 schema 限定的表名。 - SQL 层面的
PREPARE和EXECUTE、LISTEN以及会话级咨询锁均无法使用。