从PyMongo看MongoDB Read Preference

  在CAP理论与MongoDB一致性、可用性的一些思考一文中提到,MongoDB提供了一些选项,如Read Preference、Read Concern、Write Concern,对MongoDB的一致性、可用性、可靠性(durability)、性能会有较大的影响。与Read Concern、Write Concern不同的是,Read Preference基本上完全由MongoDb Driver实现,因此,本文通过PyMongo来看看Read Preference具体是如何实现的。

  本文分析的PyMongo版本是PyMongo3.6,该版本兼容MongoDB3.6及以下的MongoDB。

  本文地址:https://www.cnblogs.com/xybaby/p/10256812.html

Read Preference

Read preference describes how MongoDB clients route read operations to the members of a replica set.

  Read Prefenrece决定了使用复制集(replica set)时,读操作路由到哪个mongod节点,如果使用Sharded Cluster,路由选择由Mongos决定,如果直接使用replica set,那么路由选择由driver决定。如下图所示:

从PyMongo看MongoDB Read Preference

  MongoDB提供了以下Read Preference Mode:

primary:默认模式,一切读操作都路由到replica set的primary节点

primaryPreferred:正常情况下都是路由到primary节点,只有当primary节点不可用(failover)的时候,才路由到secondary节点。

secondary:一切读操作都路由到replica set的secondary节点

secondaryPreferred:正常情况下都是路由到secondary节点,只有当secondary节点不可用的时候,才路由到primary节点。

nearest:从延时最小的节点读取数据,不管是primary还是secondary。对于分布式应用且MongoDB是多数据中心部署,nearest能保证最好的data locality。

  这五种模式还受到和的影响。

  不同的read Preference mode适合不同的应用场景,如果数据的一致性很重要,比如必须保证read-after-write一致性,那么就需要从primary读,因为secondary的数据有一定的滞后。如果能接受一定程度的stale data,那么从secondary读数据可以减轻primary的压力,且在primary failover期间也能提供服务,可用性更高。如果对延时敏感,那么适合nearest。另外,通过tagsets,还可以有更丰富的定制化读取策略,比如指定从某些datacenter读取。

PyMongo

  首先给出pymongo中与read preference相关的类,方便后面的分析。

从PyMongo看MongoDB Read Preference

  上图中实线箭头表示强引用(复合),虚线箭头表示弱引用(聚合)

connect to replica set

  PyMongo的文档给出了如何连接到复制集:指定复制集的名字,以及一个或多个该复制集内的节点。如:

MongoClient('localhost', replicaset='foo')

  上述操作是non-blocking,立即返回,通过后台线程去连接指定节点,PyMongo连接到节点后,会从mongod节点获取到复制集内其他节点的信息,然后再连接到复制集内的其他节点。

from time import sleep
c = MongoClient('localhost', replicaset='foo'); print(c.nodes); sleep(0.1); print(c.nodes)
frozenset([])
frozenset([(u'localhost', 27019), (u'localhost', 27017), (u'localhost', 27018)])

  可以看到,刚初始化MongoClient实例时,并没有连接到任何节点(c.nodes)为空;过了一段时间,再查看,那么会发现已经连上了复制集内的三个节点。

  那么问题来了,创建MongoClient后,尚未连接到复制集节点之前,能否立即操作数据库?

If you need to do any operation with a MongoClient, such as a find() or an insert_one(), the client waits to discover a suitable member before it attempts the operation.

  通过后续的代码分析可以看到,会通过一个条件变量(threading.Condition)去协调。

PyMongo Monitor

  上面提到,初始化MongoClient对象的时候,会通过指定的mognod节点去发现复制集内的其他节点,这个就是通过monitor.Monitor来实现的。从上面的类图可以看到,每一个server(与一个mongod节点对应)都有一个monitor。Monitor的作用在于:

Health: detect when a member goes down or comes up, or if a different member becomes primary

Configuration: detect when members are added or removed, and detect changes in members’ tags

Latency: track a moving average of each member’s ping time

  Monitor会启动一个后台线程 PeriodExecutor,定时(默认10s)通过socket连接Pool给对应的mongod节点发送 ismaster 消息。核心代码(略作调整)如下

def _run(self): self._server_description = self._check_with_retry() self._topology.on_change(self._server_description) def _check_with_retry(self): address = self._server_description.address response, round_trip_time = self._check_with_socket( sock_info, metadata=metadata) self._avg_round_trip_time.add_sample(round_trip_time) # 更新rtt sd = ServerDescription( address=address, ismaster=response, round_trip_time=self._avg_round_trip_time.get()) return sd def _check_with_socket(self, sock_info, metadata=None): """Return (IsMaster, round_trip_time). Can raise ConnectionFailure or OperationFailure. """ cmd = SON([('ismaster', 1)]) if metadata is not None: cmd['client'] = metadata if self._server_description.max_wire_version >= 6: cluster_time = self._topology.max_cluster_time() if cluster_time is not None: cmd['$clusterTime'] = cluster_time start = _time() request_id, msg, max_doc_size = message.query( 0, 'admin.$cmd', 0, -1, cmd, None, DEFAULT_CODEC_OPTIONS) # TODO: use sock_info.command() sock_info.send_message(msg, max_doc_size) reply = sock_info.receive_message(request_id) return IsMaster(reply.command_response()), _time() - start

  类IsMaster是对ismaster command reponse的封装,比较核心的属性包括:

replica_set_name:从mongod节点看来,复制集的名字

primary:从mongod节点看来,谁是Priamry

all_hosts: 从mongod节点看来,复制集中的所有节点

last_write_date: mongod节点最后写入数据的时间,用来判断secondary节点的staleness

set_version:config version

election_id:只有当mongod是primary时才会设置,表示最新的primary选举编号

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjwxy.html