阅读偏好参考
在本页面
读取首选项描述 MongoDBClient 端如何将读取操作路由到replica set的成员。
默认情况下,应用程序将其读取操作定向到replica set中的primary成员(即读取首选项模式“主要”)。但是,Client 端可以指定读取首选项,以将读取操作发送到辅助对象。
读取首选项模式 | Description |
---|---|
primary | 默认模式。所有操作均从当前副本集primary读取。 |
primaryPreferred | 在大多数情况下,操作是从primary读取的,但如果不可用,则操作是从secondary成员读取的。 |
secondary | 所有操作均从副本集的secondary个成员读取。 |
secondaryPreferred | 在大多数情况下,操作是从secondary成员读取的,但是如果没有secondary成员可用,则操作是从primary读取的。 |
nearest | 无论成员的类型如何,操作都会从replica set成员中读取网络延迟最少的操作。 |
Note
读取首选项不会影响数据的可见性;即,Client 可以在写入durable之前查看写入结果:
-
不管write concern为何,使用"local"或"available"的其他 Client 端 readConcern 都可以在向发布 Client 端确认写入操作之前看到写入操作的结果。
-
使用"local"或"available" readConcern 的 Client 端可以读取随后可能是rolled back的数据。
有关 MongoDB 中读取隔离级别的更多信息,请参阅阅读隔离度,一致性和新近度。
阅读偏好也不会影响causal consistency。由因果一致的会话提供的因果一致性保证用于 MongoDB 部署的所有成员中的"majority"读关注点的读操作和"majority"读关注点的写操作。
读取首选项模式
primary读取首选项模式与使用tag sets或maxStalenessSeconds的读取首选项模式不兼容。如果您指定标签集或primary的maxStalenessSeconds
值,则驱动程序将产生错误。
primaryPreferred
当primaryPreferred读取首选项包括maxStalenessSeconds value并且没有要读取的主数据库时,Client 端通过将辅助数据库的上次写入数据与最近一次写入的辅助数据库的写入进行比较,来估计每个辅助数据库的陈旧程度。然后,Client 端将读取操作定向到次级,其估计滞后时间小于或等于maxStalenessSeconds
。
当读取的首选项包含标签集(即标签规格列表)并且没有要读取的主要对象时,Client 端将尝试查找具有匹配标签的次要成员(按 Sequences 尝试标签规范,直到找到匹配项)。如果找到匹配的次级,则 Client 端从匹配次级的nearest group中选择一个随机的次级。如果没有辅助节点具有匹配的标签,则读取操作将产生错误。
当读取的首选项包含maxStalenessSeconds
值**和标记集时,Client 端将首先按陈旧性过滤,然后按指定的标记过滤。
使用primaryPreferred模式的读取操作可能会返回陈旧数据。使用maxStalenessSeconds
选项可避免从次要版本中读取 Client 估计过时的内容。
secondary
- 操作只能从集合的secondary个成员中读取。如果没有可用的辅助副本,则此读取操作将产生错误或异常。
大多数副本集具有至少一个辅助副本,但是在某些情况下可能没有可用的辅助副本。例如,如果成员处于恢复状态或不可用,则具有primary,辅助节点和arbiter的副本集可能没有任何辅助节点。
当secondary读取首选项包括maxStalenessSeconds value时,Client 端通过将辅助节点的最后一次写入与主要节点的最后一次写入进行比较,来估计每个辅助节点的过时程度。然后,Client 端将读取操作定向到次级,其估计滞后时间小于或等于maxStalenessSeconds
。如果没有主服务器,Client 端将使用具有最新写入操作的辅助服务器进行比较。
当读取的首选项包括标签集(即标签规格列表)时,Client 端将尝试查找具有匹配标签的辅助成员(按 Sequences 尝试标签规范,直到找到匹配项)。如果找到匹配的次级,则 Client 端从匹配次级的nearest group中选择一个随机的次级。如果没有辅助节点具有匹配的标签,则读取操作将产生错误。 [2]
当读取的首选项包含maxStalenessSeconds
值**和标记集时,Client 端将首先按陈旧性过滤,然后按指定的标记过滤。
使用secondary模式的读取操作可能会返回陈旧数据。使用maxStalenessSeconds
选项可避免从次要版本中读取 Client 估计过时的内容。
当secondaryPreferred读取首选项包括maxStalenessSeconds value时,Client 端通过将辅助节点的最后一次写入与主要节点的最后一次写入进行比较,来估计每个辅助节点的过时程度。然后,Client 端将读取操作定向到次级,其估计滞后时间小于或等于maxStalenessSeconds
。如果没有主服务器,Client 端将使用具有最新写入操作的辅助服务器进行比较。如果没有估计滞后时间小于或等于maxStalenessSeconds
的辅助对象,则 Client 端会将读取操作定向到副本集的主要对象。
当读取的首选项包括标签集(即标签规格列表)时,Client 端将尝试查找具有匹配标签的辅助成员(按 Sequences 尝试标签规范,直到找到匹配项)。如果找到匹配的次级,则 Client 端从匹配次级的nearest group中选择一个随机的次级。如果没有辅助节点具有匹配的标签,则 Client 端将忽略标签并从主节点读取。
当读取的首选项包含maxStalenessSeconds
值**和标记集时,Client 端将首先按陈旧性过滤,然后按指定的标记过滤。
使用secondaryPreferred模式的读取操作可能会返回陈旧数据。使用maxStalenessSeconds
选项可避免从次要版本中读取 Client 估计过时的内容。
nearest
- 驱动程序从其网络延迟在可接受的延迟窗口内的成员读取。路由读取操作时,以nearest模式进行的读取不考虑成员是主要成员还是次要成员:主要和次要成员被同等对待。阅读的偏好member selection文档详细描述了该过程。
设置此模式可最大程度地减少网络延迟对读取操作的影响,而无需优先考虑当前或过时的数据。
当读取首选项包含maxStalenessSeconds value时,Client 端将比较辅助节点的上次写入与主节点(如果有)的最后一次写入,或者通过比较辅助节点的最近写入(如果没有主节点)与最近写入,来估计每个辅助节点的过时状态。然后,Client 端将滤除估计滞后时间大于maxStalenessSeconds
的任何辅助节点,并将读取结果随机定向到网络延迟在可接受的 await 时间窗内的其余成员(主要或辅助节点)。
如果指定tag set,则 Client 端将尝试找到与指定标签集匹配的副本集成员,并将读取定向到nearest group中的任意成员。
当读取的首选项包含maxStalenessSeconds
值 和 标记集时,Client 端首先按陈旧性过滤,然后按指定的标记过滤。然后,Client 端从其余的mongod个实例中,将读取随机地定向到可接受的 await 时间窗口内的实例。阅读的偏好member selection文档详细描述了该过程。
使用nearest模式的读取操作可能会返回陈旧数据。使用maxStalenessSeconds
选项可避免从次要版本中读取 Client 估计过时的内容。
Note
Tag Set
如果一个或多个副本集成员与tags关联,则可以在读取首选项中指定一个标记集(标记规范文档的数组)以定位这些成员。
要configure一个带有标签的成员,请将members[n].tags设置为包含标签名称和值对的文档。标签的值必须是字符串。
{ "<tag1>": "<string1>", "<tag2>": "<string2>",... }
然后,您可以在读取首选项中包含一个标记集,以定位标记的成员。标签集是标签规范文档的数组,其中每个标签规范文档都包含一个或多个标签/值对。
[ { "<tag1>": "<string1>", "<tag2>": "<string2>",... }, ... ]
为了查找副本集成员,MongoDB 会连续尝试每个文档,直到找到匹配项。有关详情,请参见标签匹配 Sequences。
例如,如果辅助成员具有以下members[n].tags:
{ "region": "South", "datacenter": "A" }
然后,以下标记集可以将读取操作定向到上述辅助节点(或具有相同标记的其他成员):
[ { "region": "South", "datacenter": "A" }, { } ] // Find members with both tag values. If none are found, read from any eligible member.
[ { "region": "South" }, { "datacenter": "A" }, { } ] // Find members with the specified region tag. Only if not found, then find members with the specified datacenter tag. If none are found, read from any eligible member.
[ { "datacenter": "A" }, { "region": "South" }, { } ] // Find members with the specified datacenter tag. Only if not found, then find members with the specified region tag. If none are found, read from any eligible member.
[ { "region": "South" }, { } ] // Find members with the specified region tag value. If none are found, read from any eligible member.
[ { "datacenter": "A" }, { } ] // Find members with the specified datacenter tag value. If none are found, read from any eligible member.
[ { } ] // Find any eligible member.
标签匹配 Sequences
如果标记集列出了多个文档,则 MongoDB 会连续尝试每个文档,直到找到匹配项为止。找到匹配项后,该标签说明文档将用于查找所有合格的匹配成员,而其余标签说明文档将被忽略。如果没有成员与任何标签规范文档匹配,则读取操作返回错误。
Tip
为了避免在没有成员符合任何标签规范的情况下出错,您可以添加一个空文档{ }
作为标签集的最后一个元素,以从任何符合条件的成员中读取。
例如,考虑以下带有三个标签规范文档的标签集:
[ { "region": "South", "datacenter": "A" }, { "rack": "rack-1" }, { } ]
首先,MongoDB 尝试查找同时标记为"region": "South"
和"datacenter": "A"
的成员。
{ "region": "South", "datacenter": "A" }
-
如果找到成员,则不会考虑其余的标签规范文档。相反,MongoDB 使用此标签规范文档来查找所有符合条件的成员。
-
另外,MongoDB 尝试查找具有第二个文档中指定的标签的成员
{ "rack": "rack-1" }
-
如果发现某个成员已标记,则不考虑其余的标记规范文档。相反,MongoDB 使用此标签规范文档来查找所有符合条件的成员。
- 否则,考虑第三份文件。
{ }
空文档匹配任何符合条件的成员。
标记设置和读取首选项模式
标签与模式primary不兼容,并且通常仅在集合中的selecting和secondary成员进行读取操作时适用。但是,nearest读取模式与标签集组合时,会选择具有最低网络延迟的匹配成员。该成员可以是主要成员或次要成员。
Mode | Notes |
---|---|
primaryPreferred | 指定的标签集仅在选择符合条件的次级标签时适用。 |
secondary | 指定的标记集始终适用。 |
secondaryPreferred | 指定的标签集仅在选择符合条件的次级标签时适用。 |
nearest | 指定的标记集适用于选择主要还是合格的次要对象。 |
有关modes和标记集之间的交互的信息,请参考特定的阅读首选项模式文档。
有关配置标签集的信息,请参见配置副本集标记集教程。
配置读取首选项
使用 MongoDB 驱动程序时,可以在连接到副本集或分片群集时指定读取首选项。例如,请参阅connection string。您还可以在更详细的级别上指定读取首选项。有关详细信息,请参见驱动程序的api documentation。
使用mongo
Shell 程序时,请参见cursor.readPref()和Mongo.setReadPref()。例如:
db.collection.find({}).readPref( "secondary", [ { "region": "South" } ] )
Use Cases
根据应用程序的要求,您可以将不同的应用程序配置为使用不同的读取首选项,或者对同一应用程序中的不同查询使用不同的读取首选项。考虑以下应用程序以实现不同的阅读偏好策略。
Maximize Consistency
为了避免陈旧的读取,请使用primary读取首选项和"majority" readConcern
。如果主服务器不可用,例如在选举期间或大多数副本集不可访问时,使用primary读取首选项的读取操作会产生错误或引发异常。
在某些情况下,副本集可能会暂时具有两个主副本。但是,只有一个主节点能够确认与"majority"写有关的写操作。
-
部分network partition可将主节点(
P
old)隔离到具有少数节点的分区中,而分区的另一侧则包含多数节点。具有多数的分区将选择一个新的主(P
new),但是在很短的时间内,旧的主(P
old)可能仍 continue 为读取和写入服务,因为它尚未检测到只能看到少数。副本集中的节点数。在此期间,如果旧的主数据库(P
old)作为主数据库仍对 Client 端可见,则从该主数据库读取的数据可能反映了陈旧的数据。 -
主节点(
P
旧)可能会变得无响应,这将触发选举,并且可以选举新的主节点(P
new),以进行读取和写入。如果无响应的主数据库(P
old)开始再次响应,则短暂可见两个主数据库。P
旧版本退出后,短暂的时期将结束。但是,在短暂的时间内,Client 端可能会从旧的主P
old 读取数据,该旧数据可以提供陈旧的数据。
要提高一致性,您可以禁用自动failover;但是,禁用自动故障转移会牺牲可用性。
Maximize Availability
要在可能的情况下允许读取操作,请使用primaryPreferred。如果有主数据库,您将获得一致的读取[6],但是如果没有主数据库,您仍然可以查询secondaries。但是,使用此读取模式时,请考虑中学 vs 中学首选中描述的情况。
Minimize Latency
要始终从低延迟节点读取,请使用nearest。驱动程序或mongos将从最近的成员读取,并且距离最近的成员不超过 15 毫秒[3]。
nearest不保证一致性。如果距离应用程序服务器最近的成员是具有某些复制滞后的次要成员,则查询可能会返回陈旧数据。 nearest仅反映网络距离,不反映 I/O 或 CPU 负载。
来自地理分布成员的查询
如果副本集的成员在地理位置上分散,则可以基于副本集标记来反映实例的位置,然后将应用程序配置为查询附近的成员。
例如,如果“东部”和“西部”数据中心中的成员分别是tagged {'dc': 'east'}
和{'dc': 'west'}
,则位于东部数据中心中的应用程序服务器可以使用以下读取首选项从附近的成员中进行读取:
db.collection.find().readPref('nearest', [ { 'dc': 'east' } ])
尽管nearest已经开始偏爱具有低网络延迟的成员,但是使用标记可以使选择更加可预测。
次要 vs 次要偏好
对于特定的专用查询(例如 ETL,报告),您可以使用secondary读取首选项模式来从主要查询转移读取负载。对于此用例,secondary模式优于secondaryPreferred模式,因为secondaryPreferred冒以下情况的风险:如果所有辅助副本都不可用,并且副本集具有足够的arbiters [1]来防止主副本降级,则主副本将接收来自 Client。如果主数据库无法处理此负载,则查询将与写入竞争。因此,请使用读取首选项secondary而不是secondaryPreferred来分发这些特定的专用查询。
[1] | 通常,避免每个副本集部署多个仲裁器。 |
读取数据库命令的首选项
因为某些database commands从数据库中读取并返回数据,所以所有官方驱动程序都为以下命令支持完整的读取偏好模式语义:
mongos使用读取首选项将命令路由到分片。
[2] | 如果您的设备具有多个辅助设备,并且您使用了secondary读取首选项模式,请考虑以下效果。如果您有一个三人副本集带有一个主节点和两个辅助节点,而一个辅助节点不可用,则所有secondary查询必须以其余的辅助节点为目标。这将使该辅助服务器上的负载增加一倍。规划并提供能力以根据需要对此进行支持。 |
[3] | 此阈值是可配置的。有关适当的设置,请参阅localPingThresholdMs mongos或驱动程序文档。 |
[4] | 仅不写入数据的“内联” mapReduce操作支持读取首选项,否则这些操作必须在primary成员上运行。 |
[5] | 使用$out 管道运算符可强制聚合管道在主数据库上运行。 |
[6] | (1,2)在some circumstances中,副本集中的两个节点可能短暂地认为它们是主要节点,但是最多只能有{ w: "majority" }个写入问题来完成写入。可以完成{ w: "majority" }写入的节点是当前的主节点,另一个节点是以前的主节点,通常由于network partition而尚未识别其降级。发生这种情况时,尽管请求了读取首选项primary,但连接到先前主服务器的 Client 端仍可能会观察到过时的数据,并且对先前主服务器的新写入最终将回滚。 |