了解 Workers、Hyperdrive 与源数据库之间的连接机制,对构建高效的 Hyperdrive 应用至关重要。
通过在 Cloudflare 网络内维护到数据库的连接池,Hyperdrive 在发送查询前可减少七次到数据库的往返:TCP 握手(1 次)、TLS 协商(3 次)和数据库认证(3 次)。
在 Cloudflare Worker 中使用数据库客户端时,连接生命周期与传统服务器环境不同。流程如下:
没有 Hyperdrive 时,每次 Worker 调用都需直接与源数据库建立新连接。连接建立需要多次互联网往返以完成 TCP 握手、TLS 协商和数据库认证——在查询执行前就有 7 次往返和额外延迟。
Hyperdrive 将连接建立分为两部分:快速的边缘连接和到数据库的优化路径。
-
边缘连接建立:Worker 代码中的数据库驱动与 Hyperdrive 实例建立连接。这在边缘、与 Worker 同址完成,创建连接非常快。因此需使用 Hyperdrive 的特殊连接字符串。
-
跨区域单次往返:由于认证已在边缘完成,Hyperdrive 只需一次跨区域往返到数据库,而非连接建立期间的多次往返。
-
从池中获取现有连接:Hyperdrive 使用靠近数据库的连接池中的现有连接,以最小化延迟。
-
无可用连接时创建新连接:按需从靠近数据库的区域创建新连接,以降低建立新连接的延迟。
-
执行查询:查询在数据库上执行,结果经 Hyperdrive 返回 Worker。
-
连接清理:Worker 处理完请求后,Worker 内的数据库客户端连接会被自动垃圾回收。但 Hyperdrive 保持到源数据库的连接在池中打开,供下次 Worker 调用复用。这意味着后续请求仍会执行快速的边缘连接建立,但会复用 Hyperdrive 在数据库附近池中的现有连接。
Worker 处理完请求后,数据库客户端会被自动垃圾回收,到 Hyperdrive 的边缘连接会被清理。Hyperdrive 保持到源数据库的底层连接在池中打开以供复用。
无需调用 client.end()、sql.end()、connection.end()(或类似方法)来清理数据库客户端。Workers 到 Hyperdrive 的连接在请求或调用结束时自动清理,包括 Workflow 或 Queue 消费者 完成时,或 Durable Objects 休眠或空闲被驱逐时。
import { Client } from "pg";
export default {
async fetch(request, env, ctx): Promise<Response> {
const client = new Client({
connectionString: env.HYPERDRIVE.connectionString,
});
await client.connect();
const result = await client.query("SELECT * FROM pg_tables");
// No need to call client.end() — Hyperdrive automatically cleans
// up the client connection when the request ends. The underlying
// pooled connection to your origin database remains open for reuse.
return Response.json(result.rows);
},
} satisfies ExportedHandler<Env>;不要在全局作用域创建数据库客户端或连接池。应在每次处理器调用内创建新客户端——Hyperdrive 的连接池确保这很快:
import { Client } from "pg";
// 🔴 Bad: Client created in the global scope persists across requests.
// Workers do not allow I/O across request contexts, so this client
// becomes stale and subsequent queries will throw hard errors.
const globalClient = new Client({
connectionString: env.HYPERDRIVE.connectionString,
});
await globalClient.connect();
export default {
async fetch(request, env, ctx) {
// ✅ Good: Client created inside the handler, scoped to this request.
// Hyperdrive pools the underlying connection to your origin database,
// so creating a new client per request is fast and reliable.
const client = new Client({
connectionString: env.HYPERDRIVE.connectionString,
});
await client.connect();
const result = await client.query("SELECT * FROM pg_tables");
return Response.json(result.rows);
},
};import { Client } from "pg";
// 🔴 Bad: Client created in the global scope persists across requests.
// Workers do not allow I/O across request contexts, so this client
// becomes stale and subsequent queries will throw hard errors.
const globalClient = new Client({
connectionString: env.HYPERDRIVE.connectionString,
});
await globalClient.connect();
export default {
async fetch(request, env, ctx): Promise<Response> {
// ✅ Good: Client created inside the handler, scoped to this request.
// Hyperdrive pools the underlying connection to your origin database,
// so creating a new client per request is fast and reliable.
const client = new Client({
connectionString: env.HYPERDRIVE.connectionString,
});
await client.connect();
const result = await client.query("SELECT * FROM pg_tables");
return Response.json(result.rows);
},
} satisfies ExportedHandler<Env>;与常规 Workers 不同,Durable Objects 可在多次请求间保持状态。若在 Durable Object 中保持数据库客户端打开,该连接将持续占用 Hyperdrive 连接池中的资源。大量长期存活的 Durable Objects 同时保持连接打开可能耗尽可用连接。
Hyperdrive 以事务池化模式运行,连接在事务期间被持有。包含多个查询的长时间事务会更快耗尽 Hyperdrive 可用连接,因为每个事务在完成前都会占用池中的连接。
请参阅限制,了解按 Workers 套餐 Hyperdrive 配置可用的连接数。