从MongoDB 8.2 开始,如果由于 数据块迁移而存在丢失文档的风险,则分片的集群中的从节点(secondary node from replica set)读取可能会自动终止。
为了支持这一新行为, MongoDB 8.2 引入了以下更改:
- 添加 - terminateSecondaryReadsOnOrphanCleanup参数(默认:- true)- 注意- 如果将 - terminateSecondaryReadsOnOrphanCleanup设立为- false,服务器不会终止读取,并且可能会由于数据块迁移而丢失分片的片集合中的文档。这是MongoDB 8.1 或更早版本中的默认行为。要学习;了解更多信息,请参阅 禁用从节点读取终止。
- 将 - orphanCleanupDelaySecs默认值从- 900秒增加到- 3600秒(1 小时)
行为
默认下,提交数据块迁移时,分片集群会执行以下操作:
- 源分片启动孤立清理进程,以删除迁移到其他分片的文档。 - 分片等待主节点 (primary node in the replica set)上任何预先存在的读取完成。 
- 分片再等待 - orphanCleanupDelaySecs秒(默认:1 小时)。
- 分片删除孤立文档。 
 
- 从节点终止在迁移完成之前开始的读取。 
- 从节点复制孤立文档删除。 
在删除孤立文档之前终止从节点(secondary node from replica set)读取,可确保长时间运行的从节点(secondary node from replica set)读取不会错过清理进程中删除的任何文档。
监控
您可以通过以下方式监控由于孤立清理而终止的从节点(secondary node from replica set)读取:
- 使用以下 - mongosh命令检查从节点(secondary node from replica set)节点的服务器状态:- db.serverStatus().metrics.operation.killedDueToRangeDeletion
- 查看您的 - mongod日志。每次终止都会产生一个日志条目,如下示例所示:
{    "t": {          "$date": "2025-06-11T12:11:43.361+02:00"    },    "s": "I",    "c": "SHARDING",    "id": 10016300,    "svc": "S",    "ctx": "conn93",    "msg": "Read has been terminated due to orphan range cleanup",    "attr": {          "type": "command",          ...          "workingMillis": 0,          "durationMillis": 0,          "orphanCleanupDelaySecs": 3600    } } 
管理长时间运行的从节点读取
如果应用程序在执行数据块迁移的分片的集群上执行从从节点(secondary node from replica set)读取超过 1 小时,则可能会因读取终止而遇到 QueryPlanKilled 错误(错误代码 175)。
管理长时间运行的从节点(secondary node from replica set)读取的推荐方法是在应用程序中实现恢复机制。
您还可以使用以下替代策略管理长时间运行的从节点(secondary node from replica set)读取:
实施恢复机制
恢复机制允许应用程序创建新的读取操作,该操作从上一个读取操作终止的位置开始。
要实现有效的恢复机制,应用程序必须对查询结果使用一致的排序顺序。为恢复机制选择排序顺序时,请考虑以下因素:
- 排序操作应利用索引字段来高效执行查询。 
- 排序字段应包含唯一值。 - 如果排序字段值不是唯一的,则应用程序必须实现额外的逻辑来处理股票相同排序值的文档。 
 
例子
考虑一个包含 zipcodes集合的 cities数据库,其结构如下:
{         "state": "NY",         "city": "NEW YORK",         "zipcode": "00501" } 
对于此示例,假设 zipcode字段值是唯一的。
以下JavaScript代码执行从节点(secondary node from replica set)读取操作以检索state 为 NY 的所有文档,并实施恢复机制以处理QueryPlanKilled 错误:
let readDoc; let latestZip; let cursor = db.getSiblingDB("cities").zipcodes.find({    state: "NY" }) .sort({zipcode: 1}) .readPref("secondary"); while(cursor.hasNext()) {    try {       readDoc = cursor.next();       // process `readDoc` here       latestZip = readDoc.zipcode;    } catch (err) {       if (err.code === 175 &&          err.errmsg.includes("Read has been terminated due to orphan range cleanup")) {          console.log("Query terminated, resuming from zipcode:", latestZip);          cursor = db.getSiblingDB("cities").zipcodes.find({                state: "NY",                zipcode: {$gt: latestZip}          })          .sort({zipcode: 1})          .readPref("secondary");       } else {          throw err; // Rethrow non-termination errors       }    } } 
查看示例数据库和应用程序逻辑时,请考虑以下事项:
- 示例代码使用按 - zipcode排序的恢复机制处理- QueryPlanKilled错误。对- zipcode字段进行排序可确保每个文档的顺序一致和唯一的排序值。这允许应用程序在读取操作被终止的地方精确地恢复。
- cities.zipcodes集合实施- {state: 1, zipcode: 1}复合索引,以确保恢复机制查询的效率。实现此复合索引可防止集合扫描和内存中排序,并支持过滤和排序操作。要学习;了解有关创建有效索引的更多信息,请参阅 ESR(相等、排序、范围)指南。
- QueryPlanKilled错误(错误代码- 175)可能由于终止从从节点(secondary node from replica set)读取以外的其他原因而发生。要准确处理- QueryPlanKilled错误,您必须解析- errmsg字段。MongoDB在终止从从节点(secondary node from replica set)读取时返回以下错误消息:
{    code: 175,    name: QueryPlanKilled,    categories: [CursorInvalidatedError],    errmsg: "Read has been terminated due to orphan range cleanup" } 
- 当应用程序由于孤立范围清理而遇到 - QueryPlanKilled错误时,它会使用上次成功处理的邮政编码作为已恢复查询的点。- $gt操作符确保应用程序不会进程重复的文档。
在测试环境中测试恢复机制并监控生产集群,以了解从从节点(secondary node from replica set)读取终止的频率。如果终止频繁发生,则可能需要调整查询模式,或考虑其他数据访问方法。要学习;了解如何监控集群是否存在这些错误,请参阅监控。
增加 orphanCleanupDelaySecs
orphanCleanupDelaySecs服务器参数控制MongoDB在从源分片删除已迁移数据块之前的等待时间。
增加 orphanCleanupDelaySecs 可以允许从节点(secondary node from replica set)读取操作运行更长的时间。您可以在初创企业和运行时设立orphanCleanupDelaySecs。
以下命令将 orphanCleanupDelaySecs 设置为 2 小时:
db.adminCommand({         setParameter: 1,         orphanCleanupDelaySecs: 7200 }) 
重要
增加 orphanCleanupDelaySecs 意味着孤立文档在节点上保留更长的时间。如果增加此值,则执行使用索引但不包含分片键的查询可能会导致性能下降,因为该查询必须在返回结果之前过滤更多的孤立文档。
禁用从节点读取终止
注意
在MongoDB 8.1 或更早版本中,分片的集群不会自动终止长时间运行的从节点(secondary node from replica set)读取。要在MongoDB 8.2 或更高版本中匹配此行为,请禁用从节点(secondary node from replica set)读取终止。
terminateSecondaryReadsOnOrphanCleanup服务器参数控制长时间运行的从节点(secondary node from replica set)读取是否在孤立文档删除之前自动终止。
您可以通过将 terminateSecondaryReadsOnOrphanCleanup 设置为 false 来禁用从节点(secondary node from replica set)读取终止。您可以在初创企业或运行时设立此参数。
以下命令将 terminateSecondaryReadsOnOrphanCleanup 设置为 false:
db.adminCommand({         setParameter: 1,         terminateSecondaryReadsOnOrphanCleanup: false }) 
警告
如果禁用此功能,并且数据块迁移会影响目标集合,则从从节点(secondary node from replica set)读取可能无法返回所有文档。
禁用负载均衡器
您可以通过禁用负载均衡器且不执行任何手动迁移来避免自动终止长时间运行的从节点(secondary node from replica set)读取。
要为特定集合禁用负载均衡器,请使用 configureCollectionBalancing 命令的 enableBalancing字段。
要将负载均衡器操作限制在特定时间,请参阅安排负载均衡窗口。
警告
长时间禁用负载均衡器可能会导致分片失衡,从而降低集群性能。仅当您的使用案例需要时才禁用负载均衡器。