PHP MongoDB 连接池 or PHP MongoDB Connection Pooling
MongoDB 驱动有连接池吗?
MongoDB PHP Driver 本身是没有实现连接池的,这是官方自己说的。
MongoDB 驱动开发团队认为传统的单线程 PHP 程序(FPM 模式)下是不需要连接池的。
所以 mongo-php-driver 理论上本身是不支持多线程 PHP 应用程序的(例如: pthreads、forking)。
那么对于异步 PHP 框架(例如: Amp, ReactPHP, Swoole, Workerman...)等有没有什么 办法 呢?
MongoDB 驱动原理摘要
根据官方的说法, mongo-php-driver 是基于 libmongoc (就是 MongoDB C Driver, 也叫 libmongoc) 开发的。
该驱动是一个 单线程客户端模式 的驱动,并不是连接池的模式,是 一对一 的连接模式。
也就是说 一个 单线程 libmongoc 客户端对象(mongoc_client_t)只会维持 一个 与 MongoDB 服务端的连接。
关于 ”连接+拓扑持久性“:
PHP 驱动自版本 1.2.0+ 版本开始,采用 “连接+拓扑持久性” 的模式
-
mongo-php-driver 将依据传递给
MongoDB\Driver\Manager构造函数的参数来计算出一个哈希值, 并创建一个与该哈希值相对应的持久化客户端连接对象(mongoc_client_t),如果该哈希值对应的连接对象存在, 那么就直接复用这个连接。 -
其中
MongoDB\Driver\Manager对象是不能跨 PHP thread 共享的(详见后面的 特别注意)。 而 Manager 构造参数对应的哈希表和持久化客户端连接对象(mongoc_client_t)都是由 MongoDB 驱动自身管理的, 是与 PHP thread 无关的,可以跨 thread 复用。 -
mongo-php-driver 将自动忽略参数
$uriOptions和$driverOptions中无法识别的选项, 但是这些额外的选项仍然会被用来计算哈希值。因此你可以传递不同的自定义选项来创建出新的连接对象。 -
mongo-php-driver 从 1.10.0 开始,增加了
disableClientPersistence选项来判断创建的连接会不会被持久化 (非持久连接对象在超出作用域范围后会被销毁)。 -
被持久化的连接会一直在驱动中保留,直到相应的
MongoDB\Driver\Manager对象超出作用域范围或是对应的进程结束。
补充说明
可以简单理解为,客户端连接对象(mongoc_client_t)是驱动内部管理的,是跨所有 PHP thread 共享的。
只要哪个 Manager 对象参数命中其中某个哈希,就会取得其关联的某个连接对象(mongoc_client_t)。
也就是说只要 参数相同,不论你创建再多的 Manager 对象,起了再多的 PHP thread,
他们始终都只会拿到同一个客户端连接对象(mongoc_client_t)。
官方对于异步 PHP 程序的建议
-
通过
MongoDB\Driver\Manage构造函数参数的不同,为每个 thread 创建不同的持久化连接 -
通过使用
disableClientPersistence(mongo-php-driver version >= 1.10.0),为每个 thread 创建不同的非持久化连接 -
你那么牛逼,你自己写一个驱动呗
特别注意
在异步PHP程序中,千万千万不要跨 thread 使用相同的连接(构造参数一致导致的,或者 fork 等导致)。
因为异步并发操作相同连接,可能会有导致 IO 错误的风险 (例如:读取服务器的响应时顺序错误)。
因此,异步程序中,必须为每个 thread 创建不同的连接!
附录:MongoDB 官方关于驱动原理的详细解答
- https://github.com/mongodb/mongo-php-driver/issues/1224
- https://www.php.net/manual/en/mongodb.connection-handling.php
吐槽
官方的基本意思就一个,我的 php 驱动就是在 c 驱动上写的,c 驱动没有的就不要指望 php 驱动能有。 我们过去,现在,将来都没有打算实现连接池的想法。你不服?不服你自己写一个呗...
