ThinkPHP 的 pgsql 驱动无法适配 CockroachDB 的分布式语义;虽能建立 TCP 连接,但在 SSL 强制、事务重试、连接健康检测、RETURNING 返回值获取等环节失败,需绕过 ORM 用原生 PostgreSQL 扩展或改用 Go+pgx 实现客户端重试与连接管理。

ThinkPHP 不能直接“配置 type=pgsql”就连上 CockroachDB —— 它会连得上,但大概率在事务、重试、连接复用或 SSL 验证环节崩掉。 原因不是协议不兼容,而是 CockroachDB 对 PostgreSQL 协议的实现有强约束(比如强制 TLS、乐观并发控制),而 ThinkPHP 的 pgsql 驱动是为单体 PostgreSQL 设计的,没处理这些分布式语义。
为什么 pgsql 配置能连通却执行失败
CockroachDB 确实兼容 PostgreSQL 协议,ThinkPHP 启用 php_pdo_pgsql 后调用 pg_connect() 能建立 TCP 连接,但后续行为会出问题:
- 默认不带 SSL 参数时,CockroachDB v22.2+ 直接拒绝连接,报错
FATAL: SSL is required,而不是 PDO 报 driver not found - 即使加了
?sslmode=disable(仅限--insecure模式),ThinkPHP 的事务封装(startTrans())不会捕获TransactionRetryWithProtoRefreshError,一次冲突就让整个请求失败 - ThinkPHP 的连接池机制(如
think\db\Connection的复用逻辑)不感知 CockroachDB 的连接健康状态,空闲连接可能被服务端静默断开,下次复用时报server closed the connection unexpectedly -
ON CONFLICT DO UPDATE和INSERT ... RETURNING虽语法支持,但 ThinkPHP 的insertGetId()底层用的是PDO::exec(),拿不到返回值,必须手写query()
绕过驱动限制:用原生 pgx 或 pgsql 手动接管连接
ThinkPHP 自带的 pgsql 驱动不适合 CockroachDB 生产接入。更可行的路径是绕过它,用 Go 写轻量 API 层(推荐),或在 PHP 中用原生 pg_query() + 显式重试逻辑接管关键事务:
- 禁用 ThinkPHP 数据库自动初始化,在控制器里手动调用
pg_connect("host=127.0.0.1 port=26257 dbname=defaultdb user=root sslmode=disable")(开发环境) - 对含写操作的请求,包装一层重试:捕获错误信息中是否含
TransactionRetryWithProtoRefreshError,有则 sleep 50ms 后重 exec 整个 SQL 块(最多 3 次) - 避免用
$this->db->transaction(),改用pg_query($conn, "BEGIN")+pg_query($conn, "COMMIT")/pg_query($conn, "ROLLBACK"),自己控制事务生命周期 - 所有
RETURNING查询必须用pg_query()+pg_fetch_row(),不能依赖insertGetId()
生产环境强烈建议用 Go + pgxpool 做数据网关
ThinkPHP 接 CockroachDB 不是配置问题,是架构错配。CockroachDB 的分布式事务、连接健康、TLS 验证、负载均衡都要求客户端有对应适配能力,而 PHP 的 pgsql 扩展和 ThinkPHP ORM 都不具备:
立即学习“PHP免费学习笔记(深入)”;
-
pgxpool.Connect()自带连接验证、空闲驱逐、TLS 配置(sslmode=verify-full&sslrootcert=/path/to/ca.crt) - 可内置重试逻辑:检测错误字符串含
TransactionRetryWithProtoRefreshError就新建pool.BeginTx()并重放 SQL - Go 服务暴露 REST/JSON API,ThinkPHP 只做 HTTP Client 调用,彻底解耦数据库协议细节
- 这样既能保留 ThinkPHP 的业务逻辑开发效率,又不牺牲 CockroachDB 的一致性与弹性
真正难的不是连上,是让事务不因一次节点抖动就失败。CockroachDB 的重试语义必须由客户端显式实现,ThinkPHP 的抽象层太厚,反而成了障碍。



















