功能差异:Amazon DocumentDB 和 MongoDB - Amazon DocumentDB
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 Amazon Web Services 服务入门

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

功能差异:Amazon DocumentDB 和 MongoDB

以下是 Amazon DocumentDB(与 MongoDB 兼容)和 MongoDB 之间的功能差异。

Amazon DocumentDB 的功能优势

隐式事务

在 Amazon DocumentDB 中,所有 CRUD 声明 (findAndModifyupdateinsertdelete) 保证原子性和一致性,即使对于修改多个文档的操作也是如此。随着 Amazon DocumentDB 4.0 的推出,现在支持为多语句和多收集操作提供 ACID 属性的显式交易。有关在 Amazon DocumentDB 中使用交易的更多信息,请参阅事务.

下面是 Amazon DocumentDB 中的操作示例,用于修改满足原子行为和一致行为的多个文档。

db.miles.update( { "credit_card": { $eq: true } }, { $mul: { "flight_miles.$[]": NumberInt(2) } }, { multi: true } )
db.miles.updateMany( { "credit_card": { $eq: true } }, { $mul: { "flight_miles.$[]": NumberInt(2) } } )
db.runCommand({ update: "miles", updates: [ { q: { "credit_card": { $eq: true } }, u: { $mul: { "flight_miles.$[]": NumberInt(2) } }, multi: true } ] })
db.products.deleteMany({ "cost": { $gt: 30.00 } })
db.runCommand({ delete: "products", deletes: [{ q: { "cost": { $gt: 30.00 } }, limit: 0 }] })

组成批量操作(如 updateManydeleteMany)的各个操作是原子操作,但整个批量操作不是原子操作。例如,如果各个插入操作成功执行而未出现错误,则整个 insertMany 操作是原子操作。如果 insertMany 操作遇到错误,insertMany 操作中每个单独的插入语句都将作为原子操作执行。如果你需要 ACID 属性insertManyupdateMany, 和deleteMany操作,建议使用交易。

更新的功能差异

Amazon DocumentDB 通过从客户要求我们构建的功能中向后开发,继续改善与 MongoDB 的兼容性。本部分包含我们在 Amazon DocumentDB 中删除的功能差异,以便客户更轻松地迁移和构建应用程序。

数组索引

自 2020 年 4 月 23 日起,Amazon DocumentDB 现在支持对大于 2,048 个字节的阵列编制索引的功能。数组中单个项目的限制仍保持为 2,048 字节,这与 MongoDB 一致。

如果您正在创建新索引,则无需操作即可利用改进的功能。如果您有现有索引,则可以通过删除索引然后重新创建索引来利用改进的功能。具有改进功能的当前索引版本为 "v" : 3

注意

对于生产集群,删除索引可能会影响您的应用程序性能。我们建议您在更改生产系统时首先进行测试并谨慎行事。此外,重新创建索引所需的时间将是集合的总体数据大小的函数。

您可以使用以下命令查询索引的版本。

db.collection.getIndexes()

此操作的输出将类似于下文。在此输出中,索引的版本是 "v" : 3,这是最新的索引版本。

[ { "v" : 3, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.test" } ]

多键索引

自 2020 年 4 月 23 日起,Amazon DocumentDB 现在支持在同一个阵列中创建具有多个键的复合索引的功能。

如果您正在创建新索引,则无需操作即可利用改进的功能。如果您有现有索引,则可以通过删除索引然后重新创建索引来利用改进的功能。具有改进功能的当前索引版本为 "v" : 3

注意

对于生产集群,删除索引可能会影响您的应用程序性能。我们建议您在更改生产系统时首先进行测试并谨慎行事。此外,重新创建索引所需的时间将是集合的总体数据大小的函数。

您可以使用以下命令查询索引的版本。

db.collection.getIndexes()

此操作的输出将类似于下文。在此输出中,索引的版本是 "v" : 3,这是最新的索引版本。

[ { "v" : 3, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.test" } ]

字符串中的 Null 字符

自 2020 年 6 月 22 日起,Amazon DocumentDB 现在支持空字符('\0') 在字符串中。

基于角色的访问控制

自 2020 年 3 月 26 日起,Amazon DocumentDB 支持对内置角色进行基于角色的访问控制 (RBAC)。要了解更多信息,请参阅 基于角色的访问控制。Amazon DocumentDB 尚不支持 RBAC 的自定义角色。

$regex 索引

自 2020 年 6 月 22 日起,Amazon DocumentDB 现在支持$regex运营商来利用索引。

要将索引与 $regex 运算符一起使用,您必须使用 hint() 命令。使用 hint() 时,您必须指定将 $regex 应用到的字段的名称。例如,如果您在字段 product 上有索引,且索引名称为 p_1,则 db.foo.find({product: /^x.*/}).hint({product:1}) 将使用 p_1 索引,但 db.foo.find({product: /^x.*/}).hint(“p_1”) 不使用该索引。您可以通过使用 explain() 命令或使用分析器记录慢速查询来验证是否选择了索引。例如 db.foo.find({product: /^x.*/}).hint(“p_1”).explain()

注意

hint() 方法一次只能与一个索引一起使用。

$regex 查询使用索引的功能,针对使用前缀但未指定 Imo 正则表达式选项的正则表达式查询进行了优化。

将索引与 $regex 结合使用时,建议您在具有高选择性的字段上创建索引,这些字段的重复值数量低于集合中总文档数的 1% 的字段。例如,如果您的集合包含 100,000 个文档,则仅在相同值出现 1000 次或更少的字段上创建索引。

嵌套文档的投影

有一个功能上的区别$project3.6 版中 Amazon DocumentDB 和 MongoDB 之间的运算符,该运算符已在 Amazon DocumentDB 4.0 中解决,但在 Amazon DocumentDB 3.6 中仍不受支持。

Amazon DocumentDB 3.6 在应用投影时仅考虑嵌套文档中的第一个字段,而 MongoDB 3.6 将解析子文档并将投影应用于每个子文档。

例如:如果投影是“a.b.c”: 1,那么行为在 Amazon DocumentDB 和 MongoDB 中都按预期工作。但是,如果预测{a:{b:{c:1}}}那么 Amazon DocumentDB 3.6 只会将预测应用于a而不是b要么c. 在 Amazon DocumentDB 4.0 中,预测{a:{b:{c:1}}}将应用于ab, 和c.

与 MongoDB 之间的功能差异

管理数据库和集合

Amazon DocumentDB 不支持管理或本地数据库,MongoDB 也不支持 MongoDBsystem.*要么startup_log集合。

cursormaxTimeMS

在 Amazon DocumentDB 中,cursor.maxTimeMS重置每个计数器getMore请求. 因此,如果 3000MSmaxTimeMS已指定,查询需要 2800MS,随后每次查询getMore请求需要 300MS,那么光标不会超时。只有当单个操作(无论是查询还是个人操作)时,游标才会超时getMore请求,需要超过指定的maxTimeMS. 此外,检查光标执行时间的清扫器以一分钟粒度运行。

explain()

Amazon DocumentDB 在利用分布式、容错、自修复的存储系统的专用数据库引擎上模拟 MongoDB 4.0 API。因此,查询计划和的输出explain()Amazon DocumentDB 和 MongoDB 之间可能有所不同。希望控制其查询计划的客户可以使用 $hint 运算符强制选择首选索引。

字段名称限制

Amazon DocumentDB 不支持点 “。” 例如,在文档字段名中,db.foo.insert({‘x.1’:1}).

Amazon DocumentDB 也不支持字段名称中的 $ 前缀。

例如,在 Amazon DocumentDB 或 MongoDB 中尝试执行以下命令:

rs0:PRIMARY< db.foo.insert({"a":{"$a":1}})

MongoDB 将返回以下内容:

WriteResult({ "nInserted" : 1 })

Amazon DocumentDB 将返回错误:

WriteResult({ "nInserted" : 0, "writeError" : { "code" : 2, "errmsg" : "Document can't have $ prefix field names: $a" } })
注意

这种功能差异有一个例外。以 $ 前缀开头的以下字段名称已列入白名单,可以在 Amazon DocumentDB 中成功使用:$id、$ref 和 $db。

索引构建

Amazon DocumentDB 在任何给定时间(前台或后台)只允许在集合中构建一个索引。如果当前正在构建索引,在同一集合上发生了 createIndex()dropIndex() 之类的操作,则新尝试的操作将失败。

在索引构建(前台或后台)完成后,有效时间 (TTL) 索引开始使文档过期。

在路径中使用空键查找

当你查找一个包含空字符串作为路径一部分的密钥时(例如x.x..b),并且对象有一个空的字符串键路径(例如{"x" : [ { "" : 10 }, { "b" : 20 } ]}) 在数组内,Amazon DocumentDB 将返回与在 MongoDB 中运行相同查找的结果不同。

在 MongoDB 中,当空字符串键不在路径查找结束时,数组内的空键路径查找正常工作。但是,当空字符串键位于路径查找的末尾时,它不会查看数组。

但是,在 Amazon DocumentDB 中,只读数组中的第一个元素,因为getArrayIndexFromKeyString将空字符串转换为0,所以字符串键查找被视为数组索引查找。

MongoDB API、操作和数据类型

Amazon DocumentDB 与 MongoDB 3.6 和 4.0 API 兼容。有关支持的功能的最新列表,请参阅 支持的 MongoDB API、操作和数据类型

mongodumpmongorestore实用程序

Amazon DocumentDB 不支持管理数据库,因此在使用mongodump要么mongorestore实用程序。当您在 Amazon DocumentDB 中使用mongorestore,除了执行还原操作外,您还需要重新创建用户角色。

结果排序

Amazon DocumentDB 不保证结果集的隐式结果排序顺序。要确保结果集的顺序,可使用 sort() 显式指定排序顺序。

以下示例根据库存字段按降序对清单集合中的项目排序。

db.inventory.find().sort({ stock: -1 })

使用$sort聚合阶段,将不保留排序顺序,除非$sort阶段是聚合管道的最后一个阶段。使用$sort结合聚合阶段$group聚合阶段,$sort聚合阶段仅应用于$first$last累加器。在 Amazon DocumentDB 4.0 中,添加了支持$push尊重之前的排序顺序$sort阶段。

可重试写入

从 MongoDB 4.2 可兼容驱动程序开始,默认情况下可重试写入处于启用状态。但是,Amazon DocumentDB 当前不支持可重试写入。该功能差异将显示在类似以下内容的错误消息中。

{"ok":0,"errmsg":"Unrecognized field: 'txnNumber'","code":9,"name":"MongoError"}

可重试写入可以通过连接字符串(例如 MongoClient("mongodb://my.mongodb.cluster/db?retryWrites=false")))或 MongoClient 构造函数的关键字参数(例如 MongoClient("mongodb://my.mongodb.cluster/db", retryWrites=False)))禁用。

下面是一个在连接字符串中禁用可重试写入的 Python 示例。

client = pymongo.MongoClient('mongodb://<username>:<password>@docdb-2019-03-17-16-49-12.cluster-ccuszbx3pn5e.us-east-1.docdb.amazonaws.com:27017/?replicaSet=rs0',w='majority',j=True,retryWrites=False)

稀疏索引

要使用您在查询中创建的稀疏索引,必须在涵盖索引的字段中使用 $exists 子句。如果你省略$exists中,Amazon DocumentDB 不使用稀疏索引。

以下是示例。

db.inventory.count({ "stock": { $exists: true }})

对于稀疏的多键索引,如果查找文档生成了一组值并且只缺少了部分索引字段,则 Amazon DocumentDB 不支持唯一键约束。例如,如果输入为 "a" : [ { "b" : 2 }, { "c" : 1 } ],则 createIndex({"a.b" : 1 }, { unique : true, sparse :true }) 不受支持,因为 "a.c" 存储在索引中。

存储压缩

Amazon DocumentDB 当前不支持对存储的数据或索引进行压缩。存储的数据和索引的数据大小可能比您使用其他选项时更大。

在 $all 表达式中使用 $elemMatch

Amazon DocumentDB 当前不支持使用$elemMatch运营商在$all表达式。一种解决方法是,您可以将 $and 运算符与 $elemMatch 结合使用,如下所示。

原始运算:

db.col.find({ qty: { $all: [ { "$elemMatch": { part: "xyz", qty: { $lt: 11 } } }, { "$elemMatch": { num: 40, size: "XL" } } ] } })

更新后的运算:

db.col.find({ $and: [ { qty: { "$elemMatch": { part: "xyz", qty: { $lt: 11 } } } }, { qty: { "$elemMatch": { qty: 40, size: "XL" } } } ] })

$ne$nin$nor$not$exists, 和$elemMatch索引

Amazon DocumentDB 当前不支持将索引与$ne$nin$nor$not$exists$distinct, 和$elemMatch运算符。因此,使用这些运算符将导致集合扫描。通过在使用以上运算符之一之前执行筛选或匹配,将减少需要扫描的数据量,从而提高性能。

$lookup

Amazon DocumentDB 支持进行相等匹配(例如,左外联接),但不支持不相关的子查询。

使用索引$lookup

您现在可以将索引与$lookup阶段运算符。根据您的使用案例,您可以使用多种索引算法来优化性能。本节将解释不同的索引算法$lookup并帮助您选择最适合自己的工作负载的服务。

默认情况下,Amazon DocumentDB 将在以下情况下使用哈希算法allowDiskUse:false在使用和排序合并时allowDiskUse:true已使用。对于某些用例,可能需要强制查询优化器使用其他算法。以下是不同的索引算法$lookup聚合运算符可以利用:

  • 嵌套循环: A nested loop plan is typically beneficial for a workload if the foreign collection is <1 GB and the field in the foreign collection has an index. If the nested loop algorithm is being used, the explain plan will show the stage as NESTED_LOOP _LOOKUP.
  • 排序合并: A sort merge plan is typically beneficial for a workload if the foreign collection does not have an index on the field used in lookup and the working dataset doesn’t fit in memory. If the sort merge algorithm is being used, the explain plan will show the stage as SORT_LOOKUP.
  • 哈希: A hash plan is typically beneficial for a workload if the foreign collection is < 1GB and the working dataset fits in memory. If the hash algorithm is being used, the explain plan will show the stage as HASH_LOOKUP.

您可以识别用于$lookup运算符通过在查询中使用解释。下面是一个示例。

db.localCollection.explain(). aggregate( [ { $lookup: { from: "foreignCollection", localField: "a", foreignField: "b", as: "joined" } } ] output { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "test.localCollection", "winningPlan" : { "stage" : "SUBSCAN", "inputStage" : { "stage" : "SORT_AGGREGATE", "inputStage" : { "stage" : "SORT", "inputStage" : { "stage" : "NESTED_LOOP_LOOKUP", "inputStages" : [ { "stage" : "COLLSCAN" }, { "stage" : "FETCH", "inputStage" : { "stage" : "COLLSCAN" } } ] } } } } }, "serverInfo" : { "host" : "devbox-test", "port" : 27317, "version" : "3.6.0" }, "ok" : 1 }

作为使用的替代方法explain()方法,您可以使用分析器来查看使用$lookup运算符。有关剖析器的更多信息,请参阅分析 Amazon DocumentDB 操作.

使用 planHint

如果你想强制查询优化器使用不同的索引算法$lookup,您可以使用planHint. 为此,请使用聚合阶段选项中的注释来强制执行不同的计划。下面是评论语法示例:

comment : { comment : “<string>”, lookupStage : { planHint : “SORT” | “HASH” | "NESTED_LOOP" } }

下面是使用planHint强制查询优化器使用HASH索引算法:

db.foo.aggregate( [ { $lookup: { from: "foo", localField: "_id", foreignField: "_id", as: "joined" }, } ], { comment : "{ \\"lookupStage\\" : { \\"planHint\\": \\"HASH\\" }}"

要测试哪种算法最适合您的工作负载,可以使用executionStats的参数explain测量的执行时间的方法$lookup在修改索引算法的同时进行阶段(即HASH/SORT/NESTED_LOOP)。

下面的示例演示如何使用executionStats来衡量的执行时间$lookup使用阶段SORT算法。

db.foo.explain(“executionStats”).aggregate( [ { $lookup: { from: "foo", localField: "_id", foreignField: "_id", as: "joined" }, } ], { comment : "{ \\"lookupStage\\" : { \\"planHint\": \\"SORT\\" }}"