PDO Pool 架构

本文描述 PDO Pool 的内部设计。 如果您正在查找使用指南,请参阅 PDO Pool:连接池

双层架构

PDO Pool 由两层组成:

1. PDO 核心(pdo_pool.c – 将连接绑定到协程的逻辑、 事务管理、语句引用计数。

2. Async Pool(zend_async_pool_t – 异步扩展中的通用资源池。 管理空闲连接队列、限制和健康检查。 它不了解 PDO – 它处理抽象的 zval 值。

这种分离允许对任何资源使用相同的池化机制, 而不仅仅是数据库。

组件图

PDO Pool -- 组件

模板连接

创建带有池的 PDO 时,核心不会打开真实的 TCP 连接。 而是创建一个模板 – 一个 pdo_dbh_t 对象,存储 DSN、用户名、密码和对驱动程序的引用。所有真实连接随后 根据此模板按需创建。

对于模板,调用 db_handle_init_methods() 而不是 db_handle_factory()。 此方法设置驱动程序的方法表(dbh->methods), 但不创建 TCP 连接或分配 driver_data

连接生命周期

池中的连接生命周期

从池中创建连接(时序图)

从池中创建连接

内部 API

pdo_pool.c – 公共函数

函数 用途
pdo_pool_create() 基于构造函数属性为 pdo_dbh_t 创建池
pdo_pool_destroy() 释放所有连接,关闭池,清空哈希表
pdo_pool_acquire_conn() 为当前协程返回连接(复用或获取)
pdo_pool_peek_conn() 返回绑定的连接而不获取(如果没有则返回 NULL)
pdo_pool_maybe_release() 如果没有事务或语句,将连接返回池中
pdo_pool_get_wrapper() getPool() 方法返回 Async\Pool PHP 对象

pdo_pool.c – 内部回调

回调 何时调用
pdo_pool_factory() 池需要新连接(池为空时获取)
pdo_pool_destructor() 池销毁连接(关闭或驱逐时)
pdo_pool_healthcheck() 定期检查 – 连接是否仍然存活?
pdo_pool_before_release() 返回池之前 – 回滚未提交的事务
pdo_pool_free_conn() 关闭驱动程序连接,释放内存

绑定到协程

连接通过 pool_connections 哈希表绑定到协程, 其中键是协程标识符,值是指向 pdo_dbh_t 的指针。

协程标识符由 pdo_pool_coro_key() 函数计算:

协程完成时的清理

当连接绑定到协程时,通过 coro->event.add_callback() 注册 pdo_pool_cleanup_callback。当协程完成(正常或出错)时, 回调自动将连接返回池中。这确保即使出现未处理的异常也不会泄漏连接。

锁定:连接锁定

连接被锁定到协程,满足以下至少一个条件时不会返回池中:

引用计数在创建语句时递增,在销毁时递减。 当两个条件都清除时,pdo_pool_maybe_release() 将连接返回池中。

工厂中的凭证管理

创建新连接时,pdo_pool_factory() 通过 estrdup() 复制 模板中的 DSN、用户名和密码字符串。这是必要的,因为 驱动程序可能在 db_handle_factory() 期间修改这些字段:

成功调用 db_handle_factory() 后,通过 efree() 释放副本。 出错时,通过 pdo_pool_free_conn() 释放, 该函数也被池的析构函数使用。

与持久连接的不兼容性

持久连接(PDO::ATTR_PERSISTENT)与池不兼容。 持久连接绑定到进程并在请求之间存活, 而池在请求级别创建连接并具有自动生命周期管理。 同时启用两个属性将导致错误。

接下来