技术干货 | MongoDB 功能详解之可重试写入(Retryable Writes)
可重试写入允许 MongoDB 驱动程序在副本集(replica set)或分片集群 (sharded cluster)中遇到网络错误或无法找到健康的主节点时,自动重试某些写入操作一次。[1]
先决条件
可重试写入有以下要求:
支持的拓扑结构
可重试写入要求副本集(replica set)或分片集群(sharded cluster),并不支持单节点实例。
支持的存储引擎
可重试写入要求支持文档级别锁定的存储引擎,例如 WiredTiger 或内存(in-memory)存储引擎。
3.6+ 驱动版本
客户端需要更新至 MongoDB 3.6 或更高版本的驱动程序。
Java 3.6+ | C# 2.5+ | Perl 2.0+ |
Python 3.6+ | Node 3.0+ | PHPC 1.4+ |
C 1.9+ | Ruby 2.5+ | Scala 2.2+ |
Go 1.8+ | Rust 2.1+ | C++ 3.6.6+ |
Swift 1.2+ |
MongoDB 版本
集群中每个节点必须是 3.6 或以上版本,每个节点的 featureCompatibilityVersion 必须是 3.6 或以上版本。有关 featureCompatibilityVersion 标志的更多信息,请参阅 setFeatureCompatibilityVersion。
写入确认
使用写关注级别(Write Concern)为 0 的写操作不可重试。
可重试写入和多文档事务
4.0 版本新特性。
事务提交和中止操作(transaction commit and abort operations)是可重试写操作。如果提交操作或中止操作遇到错误,MongoDB 驱动程序无论 retryWrites 是否设置为 false,都会重试一次操作。
事务内部的写操作无论 retryWrites 的值如何,都不能单独重试。
有关事务的更多信息,参见事务(Transactions)文档。
启用可重试写入
MongoDB 驱动程序
MongoDB 4.2 及更高版本兼容的驱动程序默认启用可重试写入(Retryable Writes)。早期的驱动程序需要使用 retryWrites=true 选项。对于使用与 MongoDB 4.2 及更高版本兼容的驱动程序的应用程序,可以省略 retryWrites=true 选项。
要禁用可重试写入,使用与 MongoDB 4.2 及更高版本兼容的驱动程序的应用程序必须在连接字符串中包含 retryWrites=false。
mongosh 默认启用可重试写入。要禁用可重试写入,使用 --retryWrites=false 命令行选项:
mongosh --retryWrites=false
可重试的写操作
以下写操作在使用确认写关注,例如:写关注(Write Concern)不能为 {w: 0} 时是可重试的。
📒 注意:
事务(transactions)内部的写操作不能单独重试。
方法 | 描述 |
插入操作 | |
单文档更新操作 [1] | |
db.collection.deleteOne()db.collection.remove()where justOne is true | 单文档删除操作 |
db.collection.findAndModify()db.collection.findOneAndDelete() | findAndModify 操作 所有 findAndModify 操作都是单文档操作 |
db.collection.bulkWrite() 包含以下写操作: | 仅包含单个文档写操作的批量写操作。可重试的批量操作可以包括指定的任意组合写操作,但不能包括任何多文档写操作,例如 updateMany |
Bulk 操作: | 仅包含单个文档写操作的批量写操作。可重试的批量操作可以包括指定的任意组合写操作,但不能包括任何多文档写操作,例如:update 其中 multi 选项设置为 true |
📒 注意:
对分片键值的更新
从 MongoDB 4.2 开始,您可以通过使用单文档的更新/查找并修改操作(要么作为可重试写入,要么作为事务 transaction)来更新文档的分片键值(除非分片键字段是不可变的 _id 字段)。详情请参阅:更改文档的分片键值(Change a Document's Shard Key Value)。
[1] (1, 2) MongoDB 4.2 将重试遇到重复键异常的某些单文档的 upsert 操作(使用 upsert:true 和 multi:false 进行更新)。有关条件,参见文档:插入时的重复键错误(Duplicate Key Errors on Upsert)。在 MongoDB 4.2 之前,MongoDB 不会重试遇到重复键错误的 upsert 操作。
行为
持久网络错误
MongoDB 可重试写入只进行一次重试尝试。这有助于处理短暂的网络错误和副本集(replica set elections)选举,但无法应对持久的网络错误。
故障转移期
如果驱动程序无法在目标副本集或分片集群分片中找到一个健康的主节点(primary),则驱动程序等待 serverSelectionTimeoutMS 毫秒以确定新的主节点,然后进行重试。可重试写入操作无法解决失败转移期超过 serverSelectionTimeoutMS 的情况。
⚠️ 警告:
如果客户端应用程序在发出写入操作后暂时无响应的时间超过 localLogicalSessionTimeoutMinutes,当客户端应用程序重新响应(无需重新启动)时,写入操作有可能会被重试并再次应用。
插入时的重复键错误
MongoDB 4.2 只有在满足以下所有条件的情况下,才会对因重复键错误而失败的单文档插入操作(即 upsert : true 和 multi: false)进行重试:
- 目标集合具有引起重复键错误的唯一索引。
- 更新匹配条件要么是:
- 一个单一的等值谓词
{ "fieldA" : "valueA" }
或者是
- 一个等值谓词的逻辑AND
{ "fieldA" : "valueA", "fieldB" : "valueB" }
- 唯一索引键模式中的字段集与更新查询谓词中的字段集匹配。
- 更新操作不会修改查询谓词中的任何字段。
以下表格中包含了服务器可能对重复键错误进行重试的插入操作示例:
*手机横屏或 PC 端浏览体验更好哦:
Unique Index Key Pattern | Update Operation | Retryable |
| | Yes |
| | Yes |
| | Yes |
| | No The query predicate on fieldA is not an equality |
| | No The update operation modifies fields specified in the query predicate. |
| | No The set of query predicate fields (fieldA) does not match the set of index key fields (_id). |
| | No The set of query predicate fields (fieldA, fieldC) does not match the set of index key fields (fieldA). |
在 MongoDB 4.2 之前,MongoDB 的可重试写入不支持对由于重复键错误而失败的 upsert 操作进行重试。
诊断
serverStatus 命令及其 mongosh shell 帮助程序 db.serverStatus() 的事务(transactions)部分包含关于可重试写入的统计信息。
针对本地数据库的可重试写入
官方的 MongoDB 4.2 系列驱动默认启用可重试写入功能。如果将应用程序升级到 4.2 系列的驱动,而应用程序又写入本地数据库(local database),则会遇到写入错误,除非显式禁用可重试写入功能。
要禁用可重试写入,请在 MongoDB 集群的连接字符串(connection string)中指定 retryWrites=false。
原文:Retryable Writes
关于译者:
司冬雨,MongoDB 中文社区成员,现就职于中国联通软件研究院,目前专注于 MongoDB 数据库运维与技术支持。
文章转载自公众号:Mongoing中文社区