读取优先级
读取优先级描述了 MongoDB 客户端如何将读取操作路由到副本集成员。.
默认情况下,应用程序将读取操作指向副本集中的主成员(即读取首选模式为"主")。但是,客户端可以指定读取首选模式将读取操作发送到从节点。
读取首选模式包括读取首选模式和可选的标签集列表,以及最大延迟时间(maxStalenessSeconds)选项。
读取首选模式
以下表格总结了读取首选模式
读取首选模式 | 描述 |
---|---|
默认模式。所有操作从当前副本集的主节点读取。 包含读取操作的分布式事务必须使用读取首选模式 | |
在大多数情况下,操作从主节点读取,但如果主节点不可用,则从从节点读取。 | |
所有操作从副本集的从节点读取。 | |
操作通常从副本集的从节点读取数据。如果副本集只有一个主节点且没有其他成员,则操作从主节点读取数据。 | |
操作从根据指定的延迟阈值随机选择的有资格的副本集成员读取,无论该成员是主节点还是从节点。在计算延迟时,操作考虑以下因素
|
有关读取偏好模式的详细描述,请参阅 读取偏好模式。
行为
除了
primary
之外的所有读取偏好模式都可能返回过时数据,因为 副本 在异步过程中从主节点复制操作。 [1] 如果您选择使用非primary
模式,请确保您的应用程序可以容忍过时数据。读取偏好不会影响数据的可见性;即,客户端可以在写入得到确认或传播到副本集的大多数成员之前看到写入的结果。有关详情,请参阅 读取隔离、一致性和最新性。
读取偏好不会影响 因果一致性。因果一致性会话为读取操作提供 因果一致性保证,这些操作具有
"majority"
读取关注度和"majority"
写入关注度的写入操作,这些保证在 MongoDB 部署的所有成员中保持。
警告
具有迁移的分片集群中的二级读取可能错过文档
在分片集群中长时间运行的二级读取如果在发生迁移,可能会错过文档。
在删除块迁移期间的块之前,MongoDB 会等待 orphanCleanupDelaySecs
,或者在涉及该块的查询在分片主节点上完成,以较长者为准。最初在节点为主时运行,但在节点降级为二级后继续运行的查询将被视为最初在二级上执行。也就是说,如果当前主节点上没有针对该块的查询,服务器只等待 orphanDelayCleanupSecs
。
如果这些查询的执行时间超过 orphanCleanupDelaySecs
,则针对块并运行在二级上的查询可能会错过文档。
读取偏好模式
primary
所有读取操作仅使用当前副本集的主节点。 [1] 这是默认的读取模式。如果主节点不可用,读取操作将产生错误或抛出异常。
primary
读取偏好模式与使用标签集列表或最大不稳定性秒数的读取偏好模式不兼容。如果您指定了标签集列表或maxStalenessSeconds
值,则驱动程序将产生错误。包含读取操作的分布式事务必须使用读取首选模式
primary
。给定事务中的所有操作必须路由到同一成员。
primaryPreferred
在大多数情况下,操作从集合的主节点读取。然而,如果在故障转移情况下主节点不可用,操作将从满足读取偏好
maxStalenessSeconds
和标签集列表的辅助节点读取。当读取偏好
primaryPreferred
包括一个最大延迟秒数(maxStalenessSeconds)值,并且没有可读取的主节点时,客户端通过比较每个副本的最后写入时间和具有最新写入的副本的最后写入时间来估计每个副本的延迟程度。然后客户端将读取操作定向到估计延迟小于或等于maxStalenessSeconds
的副本。当读取偏好包括一个标签集合列表(标签集合数组),并且没有可读取的主节点时,客户端将尝试找到具有匹配标签的副本成员(按照顺序尝试标签集合,直到找到匹配项)。如果找到匹配的副本,客户端将从匹配副本的最近组中选择一个随机副本。如果没有副本具有匹配的标签,则读取操作会产生错误。
当读取偏好包括一个
maxStalenessSeconds
值和标签集合列表时,客户端首先按延迟过滤,然后按指定的标签过滤。使用
primaryPreferred
模式的读取操作可能返回陈旧数据。使用maxStalenessSeconds
选项可以避免从客户端估计过度陈旧的副本中读取。
secondary
仅从集合的副本成员读取操作。如果没有可用的副本,则此读取操作会产生错误或异常。
大多数副本集至少有一个副本,但存在某些情况下可能没有可用的副本。例如,具有一个主节点、一个副本和一个仲裁者的副本集,如果成员处于恢复状态或不可用,则可能没有任何副本。
当
次要
读取优先级包含一个最大延迟秒数值时,客户端通过比较次要副本的最后写入时间与主副本的时间来估计每个次要副本的延迟程度。然后客户端将读取操作指向一个估计延迟小于或等于最大延迟秒数
的次要副本。如果没有主副本,客户端将使用最新写入的次要副本进行比较。当读取优先级包含一个标签集合列表(标签集合数组)时,客户端会尝试找到具有匹配标签的次要成员(按顺序尝试标签集合,直到找到匹配项)。如果找到匹配的次要副本,客户端将从一个最近的匹配组中随机选择一个次要副本。如果没有具有匹配标签的次要副本,读取操作将产生错误。
当读取偏好包括一个
maxStalenessSeconds
值和标签集合列表时,客户端首先按延迟过滤,然后按指定的标签过滤。使用
次要
模式的读取操作可能会返回过时的数据。使用最大延迟秒数
选项可以避免从客户端估计过度过时的次要副本读取数据。
secondaryPreferred
操作通常从副本集的从节点读取数据。如果副本集只有一个主节点且没有其他成员,则操作从主节点读取数据。
当
secondaryPreferred
读取优先级包含一个最大延迟秒数值时,客户端通过比较次要副本的最后写入时间与主副本的时间来估计每个次要副本的延迟程度。然后客户端将读取操作指向一个估计延迟小于或等于最大延迟秒数
的次要副本。如果没有主副本,客户端将使用最新写入的次要副本进行比较。如果没有次要副本的估计延迟小于或等于最大延迟秒数
,客户端将读取操作指向副本集的主副本。当读取偏好包括一个标签集列表(标签集的数组)时,客户端会尝试寻找具有匹配标签的次级成员(按顺序尝试标签集,直到找到匹配为止)。如果找到了匹配的次级成员,客户端会从匹配次级的最近组中随机选择一个次级。如果没有次级具有匹配的标签,客户端将忽略标签并从主节点读取。
当读取偏好包括一个
maxStalenessSeconds
值和标签集合列表时,客户端首先按延迟过滤,然后按指定的标签过滤。使用
secondaryPreferred
模式的读取操作可能返回过时数据。使用maxStalenessSeconds
选项可以避免从客户端估计过于过时的次级节点读取。
nearest
驱动程序从网络延迟落在可接受延迟窗口内的成员读取。在
nearest
模式下的读取操作不考虑成员是主节点还是次级节点,在路由读取操作时:主节点和次级节点被同等对待。将此模式设置为最小化网络延迟对读取操作的影响,而不考虑当前或过时数据。
当读取偏好包括一个最大过时秒值时,客户端通过比较次级节点最后一次写入与主节点的写入(如果可用)或次级节点中最新写入的次级节点来估计每个次级的过时程度。然后客户端将过滤掉任何估计滞后大于
maxStalenessSeconds
的次级,并将读取随机指向剩余成员(主节点或次级节点),其网络延迟落在可接受延迟窗口内。如果您指定了一个标签集列表,客户端会尝试找到与指定的标签集列表匹配的副本集成员,并将读取操作指向最近的组中的任意成员。
当读取偏好包括一个
maxStalenessSeconds
值以及一个标签集列表时,客户端首先根据陈旧度过滤,然后根据指定的标签过滤。在剩余的mongod
实例中,客户端随后随机将读取操作导向一个位于可接受延迟窗口内的实例。读取偏好的 成员选择 文档详细描述了此过程。使用
nearest
模式进行的读取操作可能会返回陈旧数据。使用maxStalenessSeconds
选项以避免从客户端估计过于陈旧的副本上读取。
配置读取偏好
在使用 MongoDB 驱动程序时,您可以使用驱动程序的读取偏好 API 指定读取偏好。请参阅驱动程序的 API 文档。您也可以在连接到副本集或分片集群时设置读取偏好。例如,请参阅 连接字符串。
对于给定的读取偏好,MongoDB 驱动程序使用相同的 成员选择逻辑。
在使用 mongosh
时,请参阅 cursor.readPref()
和 Mongo.setReadPref()
。
读取偏好和事务
包含读取操作的分布式事务必须使用读取首选模式primary
。给定事务中的所有操作必须路由到同一成员。
其他注意事项
在使用 $merge
或 $out
阶段进行 聚合管道 时,请考虑以下点:
从 MongoDB 5.0 开始,如果集群中的所有节点都将 功能兼容性版本 设置为
5.0
或更高版本,并且 读取偏好 允许读取副本,则具有$merge
阶段的管道可以在副本集 二级 节点上运行。在早期 MongoDB 版本中,具有
$out
或$merge
阶段的管道始终在主节点上运行,并且不考虑读取偏好。
对于 mapReduce
操作,只有不写入数据的 "inline" mapReduce
操作支持读取偏好。否则,mapReduce
操作在 主成员 上运行。
[1] | (1, 2) 在某些情况下,副本集中可能有两个节点会暂时性地认为自己是主节点,但最多只有一个节点能够完成带有{ w: "majority" } 写入关注度的写操作。能够完成带有{ w: "majority" } 写入操作的节点是当前的主节点,而另一个节点是尚未意识到自己降级的旧主节点,这通常是由于网络分区。当这种情况发生时,尽管客户端请求了primary 读取偏好,但它们仍可能观察到旧数据,并且对旧主节点的新的写操作最终会回滚。 |