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

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

功能差异: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 操作中每个单独的插入语句都将作为原子操作执行。如果您需要 insertManyupdateManydeleteMany 操作的 ACID 属性,建议使用事务。

更新的功能差异

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 现在支持字符串中的 null 字符 ( '\0' )。

基于角色的访问控制

自 2020 年 3 月 26 日起,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 次或更少的字段上创建索引。

嵌套文档投影

在 3.6 版本中,Amazon DocumentDB 与 MongoDB 之间存在带 $project 运算符的功能差异,该差异已在 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,而非 bc。在 Amazon DocumentDB 4.0 中,投影 {a:{b:{c:1}}} 将应用于 abc

与 MongoDB 之间的功能差异

Amazon DocumentDB 不支持$vectorSearch作为独立运营商。相反,我们在$search运营商vectorSearch内部支持。有关更多信息,请参阅 向量搜索 Amazon DocumentDB

OpCountersCommand

Amazon DocumentDB 的OpCountersCommand行为偏离于 MongoDB 的opcounters.command 如下:

  • MongoDB 的opcounters.command 计入除插入、更新和删除之外的所有命令,而 Amazon DocumentDB 的 OpCountersCommand 也排除 find 命令。

  • Amazon DocumentDB 将内部命令(例如getCloudWatchMetricsV2)对 OpCountersCommand 计入。

管理数据库和集合

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

cursormaxTimeMS

在 Amazon DocumentDB 中,cursor.maxTimeMS 重置每个请求的计数器。getMore因此,如果指定了 3000MS maxTimeMS,则该查询耗时 2800MS,而每个后续getMore请求耗时 300MS,则游标不会超时。游标仅在单个操作(无论是查询还是单个getMore请求)耗时超过指定值时才将超时maxTimeMS。此外,检查游标执行时间的扫描器以五 (5) 分钟间隔尺寸运行。

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() 之类的操作,则新尝试的操作将失败。

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

在构建索引完成后,有效时间 (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 兼容。有关支持的功能 up-to-date 列表,请参阅支持的 MongoDB API、操作和数据类型

mongodumpmongorestore 实用程序

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

注意

我们对 Amazon DocumentDB 推荐高达且包括版本 100.6.1 的 MongoDB 数据库工具。您可以在此处下载 MongoDB 数据库工具。

结果排序

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" 存储在索引中。

在 $all 表达式中使用 $elemMatch

当前,Amazon DocumentDB 不支持在 $all表达式中使用 $elemMatch 运算符。一种解决方法是,您可以将 $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 运算符一起使用的功能。因此,使用这些运算符将导致集合扫描。通过在使用以上运算符之一之前执行筛选或匹配,将减少需要扫描的数据量,从而提高性能。

Amazon DocumentDB 增加支持在 Amazon DocumentDB 5.0 和弹性$elemMatch集群中用 运算符进行索引扫描。如果单纯查询筛选器具有一个级别的 $elemMatch 筛选器,则支持索引扫描,但如果包含一个嵌套 $elemMatch 查询,则不支持索引扫描。

$elemMatch支持在 Amazon DocumentDB 5.0 中索引扫描的查询形状:

db.foo.find( { "a": {$elemMatch: { "b": "xyz", "c": "abc"} } })

$elemMatch不支持在 Amazon DocumentDB 5.0 中索引扫描的查询形状:

db.foo.find( { "a": {$elemMatch: { "b": {$elemMatch: { "d": "xyz", "e": "abc"} }} } })

$lookup

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

配合 $lookup 使用索引

现在,您可以配合 $lookup 阶段运算符使用索引。根据您的用例,存在您可以用来优化性能的多种索引化算法。这个部分将解释用于 $lookup 的不同索引化算法,并帮助您选择最适合自己工作负载的索引化算法。

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

  • 嵌套循环:如果外部集合小于 1 GB 且外部集合中的字段具有索引,则嵌套循环计划通常有益于工作负载。如果正在使用嵌套循环算法,则解释计划将该阶段显示为NESTED_LOOP_LOOKUP

  • 排序合并:如果外部集合对查找中所用字段没有索引并且工作数据集不适合内存,则排序合并计划通常有益于工作负载。如果正在使用排序合并算法,则解释计划将该阶段显示为SORT_LOOKUP

  • 哈希:如果外部集合小于 1GB 且工作数据集适合内存,则哈希计划通常有益于工作负载。如果正在使用哈希算法,则解释计划将该阶段显示为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\\" }}"

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

以下示例说明使用 SORT 算法,如何使用 executionStats 计量 $lookup 阶段的执行时间。

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