诡异的mongodb客户端连接和读偏好

Mongodb作为一个分布式数据库,和其他分布式软件一样,客户端的作用是很大的,什么意思呢,为了使用好mongodb,不管要考虑服务器端,客户端的选择也很重要,它们是一体的。

在测试的时候可能用mongo shell,而实际编程的时候要选择对应的MongoDB driver,比如php驱动,不同驱动的行为是不一样的,除了参考手册,还要强化实践。

比如针对Read Preference maxStalenessSeconds来说,mongo shell是不支持的,这进一步说明各个客户端之间是有差异的。

本文描述PHP mongodb扩展是如何处理的。

一般连接的时候,会这样写:

$client = new MongoDB\Client("mongodb://ip1:27017,ip2:27017,ip3:27017", [
'readPreference' => 'secondary',
]);

对于一个三节点mongodb来说,连接的时候最好连三个节点,虽然连接一个节点的时候,该节点也会知晓其他节点的meta信息,但对于客户端来说,每个节点都需要连接的。

如果连接的时候配置一个节点,一旦节点挂了,那么应用程序就没法用了,另外连接多个节点,也能进行负载均衡,比如将请求分配到多个secondary节点。

结果测试,得出几个结论:

1:如果连接primary节点,readPreference即使选择secondary,实际上接收请求的还是primary。

2:这条和上面这条有点类似,如果连接一个节点,不管readPreference选择什么,它只会向连接的节点发送请求。

3:如果配置的节点大于1,则所有的节点客户端也是会连接的。

4:readPreference默认选择primary节点,所以即使下面这样连接的是secondary,最终的请求还是primary处理的。

$client = new MongoDB\Client("mongodb://ip2:27017,ip3:27017", [
]);

5:如果连接两个secondary节点,比如:

$client = new MongoDB\Client("mongodb://ip2:27017,ip3:27017", [
'readPreference' => 'secondary'
]);

则两个secondary节点会轮询接收到请求。

6:maxStalenessSeconds

$client = new MongoDB\Client("mongodb://ip2:27017,ip3:27017", [
'readPreference' => 'secondary',
'maxStalenessSeconds' => 91

]);

maxStalenessSeconds 表示如果secondary节点同步太慢,则该节点不响应请求(等同于脏数据太多)。

如果连接的节点超过1,执行上面的代码,服务器会输出 No suitable servers found (serverSelectionTryOnce set),说明没有可用的节点可用。

可如果代码改为:

$client = new MongoDB\Client(“mongodb://ip2:27017”, [
       ‘readPreference’ => ‘secondary’,
       ‘maxStalenessSeconds’ => 91
   ]);

则最终还是会拿到secondary节点的数据,这说明在连接一个节点的情况下,会忽略maxStalenessSeconds参数,优先让客户端拿到数据

关于maxStalenessSeconds,参考php官方手册,www.php.net/manual/zh/class.mongodb-driver-readpreference.php:

The minimum value for the “maxStalenessSeconds” option is 90 seconds. The driver estimates secondaries’ staleness by periodically checking the latest write date of each replica set member. Since these checks are infrequent, the staleness estimate is coarse. Thus, the driver cannot enforce a max staleness value of less than 90 seconds.

这个值本身不是特别精准的。

以上的代码mongodb 扩展是1.4。

那么mongo shell 连接会有什么情况呢,和php mongodb还是有不同的:

本文MongoDB shell version v4.2.5。

1:不管连接几个节点,比如:

mongo "mongodb://ip1:27017,ip2:27017"

默认就只会从第一个连接串连接。

2:连接primary节点,然后:

db.test.find().readPref("secondaryd")

也还是请求primary,这进一步说明,在节点内部,不会路由请求到其他节点。

但如果连接secondaryd节点,且执行:

db.test.find().readPref("primary")

则会返回 not master and slaveOk=false,至少是内部路由了primary节点。

整体来看,客户端访问mongodb的行为是不一样的,而mongodb shell只能参考,具体应用的时候开始要编码使用语言级别的扩展。