优化副本集读取性能的最佳实践

通过读取偏好、读取关注点、延迟监控、索引和超时控制来提升 MongoDB 副本集的读取性能。

优化副本集读取性能的最佳实践

MongoDB 副本集提供了高可用性,但并不会自动加快读取速度。如果您的应用程序将所有查询发送到主节点、使用弱索引或从延迟的从节点读取数据,用户会感受到页面加载缓慢和数据陈旧。

良好的读取性能来自于选择合适的读取偏好、将读取关注点与所需的一致性匹配、监控复制延迟以及首先修复慢查询。

理解副本集中的读取路径

在标准副本集部署中,一个成员被指定为主节点,处理所有写入操作。其余成员是从节点,它们异步复制主节点的数据。应用程序的读取操作可以定向到主节点,也可以根据配置分布到从节点。

优化读取意味着在即时数据一致性(通常需要从主节点读取)与减轻主节点负载(通过从从节点读取)之间取得平衡。

1. 战略性地使用读取关注点

读取关注点定义了读取操作所需的数据一致性程度。在宽松的读取关注点足够的情况下设置过于严格的读取关注点,是导致读取延迟的常见原因,因为它可能强制操作等待来自多个节点的确认。

可用的读取关注点

MongoDB 提供了几种读取关注点,每种都在延迟和持久性/一致性之间进行权衡:

读取关注点 描述 使用场景
majority 返回已被大多数投票节点确认为已提交的数据。标准默认值。 需要高持久性的通用读取。
local 返回所读取成员上可用的最新数据,无论写入确认如何。 可以容忍一些陈旧数据的读取(例如,仪表板计数器)。
linearizable 从主节点读取,并反映在读取开始之前已确认的所有写入。它需要 readConcern: "linearizable" 以及相关写入的多数写入关注点。 必须观察最新确认状态的罕见读取,例如锁所有权检查。

优化技巧:默认使用 localmajority

对于非关键读取(如加载不常更新的配置数据或缓存结果),在从节点上使用 local 读取关注点。这可以避免任何同步延迟。

示例:在会话级别设置读取关注点

// 为此特定会话设置读取关注点为 'local'
const session = mongoClient.startSession({ readConcern: { level: "local" } });

// 使用该会话执行查找操作
db.collection('mydata').find().session(session).toArray();

警告: 在从节点上使用 local 关注点读取可能会返回相对于主节点过时的数据。

2. 将读取分布到从节点

默认情况下,MongoDB 将读取定向到主节点。为了扩展读取容量,您必须使用读取偏好设置显式地将读取定向到从节点。

理解读取偏好

读取偏好决定了副本集中哪些成员有资格满足读取请求,以及它们的选择顺序。

常见的读取偏好包括:

  • primary:(默认)只有主节点有资格。
  • primaryPreferred:首先尝试主节点;如果主节点不可用,则回退到从节点。
  • secondary:只有从节点有资格。如果没有可用的从节点,操作将失败。
  • secondaryPreferred:优先选择从节点;如果没有可用的从节点,则回退到主节点。
  • nearest:选择与客户端网络延迟最低的成员(主节点或从节点)。

优化技巧:使用 secondaryPreferrednearest

对于大多数读取密集型应用程序,使用 secondaryPreferred 可以将查询负载分布到所有可用的从节点,从而显著减少主节点的负载。

如果您有地理分布的应用程序服务器,nearest 通常是最佳选择,因为它最小化了客户端的网络延迟,即使偶尔会命中主节点。

示例:使用 secondaryPreferred 连接

在连接应用程序驱动程序时,指定读取偏好:

const uri = "mongodb://host1,host2,host3/?replicaSet=rs0&readPreference=secondaryPreferred";
// 或者在驱动程序设置中使用连接选项
const options = {
  readPreference: "secondaryPreferred"
};

3. 管理从节点同步和延迟

如果您将读取路由到从节点,这些读取的性能完全取决于从节点跟上主节点的速度。高复制延迟意味着从节点提供的是过时数据,如果延迟过高,读取可能会失败或超时。

监控复制延迟

始终监控主节点和从节点之间的 optime 差异。rs.status() 显示每个成员的复制状态,而 MongoDB Atlas、Cloud Manager 或 Ops Manager 等管理工具可以在延迟时发出警报。

rs.status().members.map(m => ({
  name: m.name,
  stateStr: m.stateStr,
  optimeDate: m.optimeDate
}))

写入关注点对从节点性能的影响

虽然本文重点讨论读取,但高写入关注点设置可能会通过减慢主节点速度间接影响读取性能,进而导致从节点进一步落后。

例如,要求 w: "majority" 意味着客户端在写入到达大多数投票数据承载成员之前不会收到确认。如果从节点由于磁盘或网络压力而缓慢,应用程序写入延迟可能会增加,而这些同样过载的从节点也可能提供缓慢的读取。

写入关注点的最佳实践(间接读取优化): 不要为了看起来读取更快而降低写入关注点。根据持久性要求选择写入关注点,然后修复延迟的原因:慢磁盘、过载的从节点、过小的 oplog、网络问题或与复制竞争的查询。

4. 索引和查询优化

没有配置设置可以克服编写不良的查询。快速读取的基本原则仍然是强大的索引。

关键索引考虑因素

  1. 覆盖查询: 设计可以完全由索引满足而无需从磁盘获取文档的查询。这些是最快的读取。
  2. 索引对齐: 确保索引与 find()sort()projection() 子句中使用的字段匹配。
  3. 避免集合扫描: 始终在查询分析器中验证读取操作是否使用索引(IXSCAN),而不是执行完整的集合扫描(COLLSCAN)。

调整查询超时

如果应用程序命中一个严重延迟的从节点,查询可能会超时。在应用程序中配置合理的超时时间,以优雅地处理临时延迟,例如回退到主节点或稍后重试,而不是无限期挂起。

读取优化步骤总结

为了在 MongoDB 副本集中实现最佳读取性能,请遵循以下可操作步骤:

  1. 识别读取类型: 将读取分类为需要新鲜主节点数据的读取和可以容忍从节点最终一致性的读取。
  2. 配置读取偏好: 将连接字符串或会话选项设置为对大多数应用程序流量使用 secondaryPreferrednearest
  3. 监控延迟: 通过 rs.status()、驱动程序指标或监控平台持续监控复制延迟。如果延迟持续偏高,请调查从节点的硬件或网络问题。
  4. 审查写入关注点: 确保写入关注点不会不必要地减慢主节点速度,从而剥夺从节点的新鲜数据。
  5. 全面索引: 验证所有频繁执行的读取路径是否使用了高效的索引。

当您对数据陈旧性保持诚实时,副本集读取扩展效果最佳。将用户关键读取发送到主节点以确保数据最新,使用从节点处理可以容忍延迟的分析或仪表板,并随着流量变化持续测量查询计划和复制健康状况。