本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
功能差异:Amazon DocumentDB 和 MongoDB
下面是 Amazon DocumentDB(与 MongoDB 兼容)与 MongoDB 之间的功能差异。
Amazon DocumentDB 的功能优势
隐式事务
在 Amazon DocumentDB 中,所有 CRUD 语句( findAndModify
、update
、insert
、delete
)均保证原子性和一致性,即使对于修改多个文档的操作也是如此。随着 Amazon DocumentDB 4.0 推出,现在支持为多语句操作和多集合操作提供 ACID 属性的显式事务。有关 Amazon DocumentDB 中使用事务的更多内容,请参阅 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 }] })
组成批量操作(如 updateMany
和 deleteMany
)的各个操作是原子操作,但整个批量操作不是原子操作。例如,如果各个插入操作成功执行而未出现错误,则整个 insertMany
操作是原子操作。如果 insertMany
操作遇到错误,insertMany
操作中每个单独的插入语句都将作为原子操作执行。如果您需要insertMany
updateMany
、和deleteMany
操作的 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
查询使用索引的功能,针对使用前缀但未指定 i
、m
或 o
正则表达式选项的正则表达式查询进行了优化。
将索引与 $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
,而非 b
或 c
。在 Amazon DocumentDB 4.0 中,投影 {a:{b:{c:1}}}
将应用于 a
、b
和 c
。
与 MongoDB 之间的功能差异
主题
$vectorSearch
运算符
Amazon DocumentDB 不支持 $vectorSearch
作为独立运算符。相反,我们支持 $search
运算符内的 vectorSearch
。有关更多信息,请参阅 Amazon DocumentDB 向量搜索。
OpCountersCommand
Amazon DocumentDB 的OpCountersCommand
行为偏离于 MongoDB 的opcounters.command
如下:
MongoDB 的
opcounters.command
计入除插入、更新和删除之外的所有命令,而 Amazon DocumentDB 的OpCountersCommand
也排除find
命令。Amazon DocumentDB 将一些内部命令计入
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 只允许在一个集合中构建一个索引。或在前台,或在后台。如果当前正在构建索引,在同一集合上发生了 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
,从而字符串键查找作为数组索引查找处理。
M APIs ongoDB、操作和数据类型
亚马逊 DocumentDB 与 MongoDB 3.6、4.0 和 5.0 兼容。 APIs有关支持的功能 up-to-date列表,请参阅在 Amazon Document APIs DB 中支持 MongoDB、操作和数据类型。
mongodump
和 mongorestore
实用程序
Amazon DocumentDB 不支持管理数据库,因此在使用 mongodump
或 mongorestore
实用程序时不转储或还原管理数据库。当您在 Amazon DocumentDB 中使用 mongorestore
创建新的数据库时,除了执行还原操作外,还需要重新创建用户角色。
注意
我们对 Amazon DocumentDB 推荐高达且包括版本 100.6.1 的 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
时将使用排序合并。
注意
find
命令目前不支持 allowDiskUse
选项。该选项仅支持作为聚合的一部分。建议将聚合框架与 allowDiskUse:true
配合使用,以处理可能超过内存限制的大型查询。
对于某些用例,可能需要强制查询优化器来使用一个不同算法。以下是 $lookup
聚合运算符可以使用的不同索引化算法:
嵌套循环:如果外部集合小于 1 GB 且外部集合中的字段具有索引,则嵌套循环计划通常有益于工作负载。如果正在使用嵌套循环算法,则解释计划将该阶段显示为
NESTED_LOOP_LOOKUP
。排序合并:如果外部集合对查找中所用字段没有索引并且工作数据集不适合内存,则排序合并计划通常有益于工作负载。如果正在使用排序合并算法,则解释计划将该阶段显示为
SORT_LOOKUP
。哈希:如果外部集合小于 1 GB 且工作数据集适合内存,则哈希计划通常对工作负载有利。如果正在使用哈希算法,则解释计划将该阶段显示为
HASH_LOOKUP
。
您可以通过在查询中使用explain
来识别用于运算$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\\" }}"