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

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

功能差异:Amazon DocumentDB 和 MongoDB

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

Amazon DocumentDB B 的功能优势

隐式事务

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

以下是 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 特性insertMany,updateMany,以及deleteMany操作,建议使用事务。

更新的功能差异

Amazon DocumentDB 继续改善与 MongoDB 的兼容性,它与客户要求我们构建的功能背道而驰。本部分包含我们在 Amazon DocumentDB 中移除的功能差异,以便我们的客户更轻松地迁移和构建应用程序。

数组索引

截至 2020 年 4 月 23 日,Amazon DocumentDB B 现在支持索引大于 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 B 现在支持使用同一数组中的多个键创建复合索引。

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

注意

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

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

db.collection.getIndexes()

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

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

字符串中的 Null 字符

截至 2020 年 6 月 22 日,Amazon DocumentDB B 现在支持空字符 ('\0') 以字符串表示。

基于角色的访问控制

Amazon DocumentDB 支持内置角色的访问控制(RBAAC)。要了解更多信息,请参阅 基于角色的访问控制。Amazon DocumentDB B 尚不支持 RBAC 的自定义角色。

$regex 索引

截至 2020 年 6 月 22 日,Amazon DocumentDB B 现在支持$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 次或更少的字段上创建索引。

嵌套文档的投影

有功能上的区别$project版本 3.6 中Amazon DocumentDB B 和 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 B 3.6 只会将投影应用于a而不是b要么c. 在Amazon DocumentDB B 4.0 中,投影{a:{b:{c:1}}}将应用于a,b,以及c.

与 MongoDB 之间的功能差异

管理数据库和集合

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

cursormaxTimeMS

Amazon DocumentDBcursor.maxTimeMS重置每个计数器getMore请求。因此,如果 3000MSmaxTimeMS已指定,查询需要 2800MS,每个后续查询getMore请求需要 300 毫秒,则游标不会超时。只有在单个操作(无论是查询还是个别操作)时,游标才会超时getMore请求,花费超过指定值maxTimeMS. 此外,检查游标执行时间的扫描器以一分钟的粒度运行。

explain()

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

字段名称限制

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

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

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

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

MongoDB 将返回以下内容:

WriteResult({ "nInserted" : 1 })

Amazon DocumentDB B 将返回一个错误:

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

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

索引构建

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

默认情况下,Amazon DocumentDB B 和 MongoDB 版本 4.0 中的索引构建在后台进行。如果向 createIndexes 或其 shell 助手指定 MongoDB 版本 4.2 及更高版本会忽略后台索引构建选项createIndex()createIndexes().

生存时间 (TTL) 索引在索引构建完成后开始使文档过期。

在路径中使用空键查找

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

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

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

MongoDB API、操作和数据类型

Amazon DocumentDB B 与 MongoDB 3.6 和 4.0 API 兼容。对于 up-to-date 支持的功能列表,请参阅支持的 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 B 4.0 中,添加了对以下内容的支持$push遵守先前的排序顺序$sort阶段。

可重试写入

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

{"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 B 不使用稀疏索引。

以下是示例。

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 B 目前不支持对存储的数据或索引进行压缩。存储的数据和索引的数据大小可能比您使用其他选项时更大。

在 $all 表达式中使用 $elemMatch

Amazon DocumentDB B 目前不支持使用$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 B 目前不支持将索引与$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 哈希_LOOKUP.

您可以识别正在使用的索引算法$lookup运算符在查询中使用 explain。下面是一个示例。

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\\" }}"