本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
功能差异:亚马逊 DocumentDB 和 MongoDB
下面是 Amazon DocumentDB(与 MongoDB 兼容)与 MongoDB 之间的功能差异。
亚马逊 DocumentDB 的功能优势
在 Amazon DocumentDB 中,所有CRUD语句(
findAndModify
update
insert
、、、
delete
)都保证原子性和一致性,即使是修改多个文档的操作也是如此。随着 Amazon DocumentDB 4.0 的推出,现在支持为多语句和多集合操作提供ACID属性的显式交易。有关 Amazon DocumentDB 中使用事务的更多内容,请参阅
亚马逊 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, "key" : { "_id" : 1 "name" : "_id_", "ns" : "test.test""v" : 3
,这是最新的索引版本。自 2020 年 4 月 23 日起,Amazon DocumentDB 现在支持在同一个数组中创建具有多个键的复合索引的功能。
如果您正在创建新索引,则无需操作即可利用改进的功能。如果您有现有索引,则可以通过删除索引然后重新创建索引来利用改进的功能。具有改进功能的当前索引版本为
"v" : 3
。注意
对于生产集群,删除索引可能会影响您的应用程序性能。我们建议您在更改生产系统时首先进行测试并谨慎行事。此外,重新创建索引所需的时间将是集合的总体数据大小的函数。
您可以使用以下命令查询索引的版本。
db.collection.getIndexes()
此操作的输出将类似于下文。在此输出中,索引的版本是
"v" : 3, "key" : { "_id" : 1 "name" : "_id_", "ns" : "test.test""v" : 3
,这是最新的索引版本。自 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 在专门构建的数据库引擎API上模拟 MongoDB 4.0,该引擎利用分布式、容错、自我修复的存储系统。因此,查询计划和
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 中的索引构建在后台进行。MongoDB 版本 4.2 及更高版本将忽略后台索引构建选项(如果指定 createIndexes 给或其 shell 助手和)。createIndex()
createIndexes()
Time to Live (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 兼容。APIs有关支持的功能 up-to-date 列表,请参阅在 Amazon Document APIs DB 中支持 MongoDB、操作和数据类型。
mongodump
和mongorestore
公用事业
Amazon DocumentDB 不支持管理数据库,因此在使用 mongodump
或 mongorestore
实用程序时不转储或还原管理数据库。当您在 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"