MongoDB数据建模-第三章:查询文档-2.写入操作

.2写入操作

在MongoDB中,我们有三种写操作:insert,update和remove。为了运行这些操作,MongoDB提供了三个接口:db.document.insert,db.document.update和db.document.remove。 MongoDB中的写入操作针对特定的集合,并且在单个文档的层次上是原子的。

当我们在MongoDB中为文档建模时,写入操作与读取操作同样重要。 在单文档级别的原子性可以决定我们是否嵌入文档。 我们将在第7章 伸缩性(Scaling)中更详细地介绍这一点,但选择分片键的活动对于我们是否编写操作的性能是决定性的,因为根据键的选择,我们将写入一个或多个分片。

另外,写作操作性能的另一个决定因素与MongoDB物理模型有关。 10gen提供了许多建议,但让我们专注于那些对我们的开发产生最大影响的建议。由于MongoDB的更新模型基于随机I/O操作,因此建议您使用固态硬盘或SSD。就随机I/O操作而言,固态硬盘与旋转磁盘相比具有优越的性能。尽管旋转磁盘更便宜,并且基于这种硬件扩展基础架构的成本也不是那么昂贵,但使用SSD或增加RAM仍然更有效。对此主题的研究表明,对应随机I/O操作,SSD的性能优于旋转磁盘 100倍。

理解写操作的另一个重要的事情是文档如何由MongoDB实际写在磁盘上。 MongoDB使用日志记录机制来进行写入操作,并且此机制在将日志写入数据文件之前使用日志写入更改操作。这是非常有用的,特别是当我们有一个“肮”关闭。当mongod进程重新启动时,MongoDB将使用日志文件将数据库状态恢复到一致状态。

正如第2章用MongoDB进行数据建模所述,BSON规范允许我们拥有一个最大大小为16 MB的文档。从2.6版本开始,MongoDB使用空间分配策略来创建记录或文档,名为"两种大小的分配权力"。顾名思义,考虑到文档的最小尺寸是32个字节,MongoDB将为每个文档以字节形式分配一个大小为2的幂的大小空间(例如,32,64,128,256,512,...)。 这种策略比文档真正需要分配了更多的空间,也给了它更多的空间去增长。

3.2.1插入操作(Insert)

插入接口是在MongoDB中创建新文档的可能方式之一。 插入接口语法以下:

db.collection.insert(

,

{

writeConcern: ,

ordered:

}

)

其中,

document or array of documents可以是文档或数组,其中应该在目标集合中创建一个或多个文档。

writeConcern是一个表达写入关注点的文档。

ordered应该是一个布尔值,如果为true,则会对数组的文档执行有序处理,如果文档中有错误,MongoDB将停止处理它。否则,如果该值为假,它将执行无序的过程,并且如果发生错误则不会停止。默认情况下,该值为true。

在下面的例子中,我们可以看到如何使用插入操作:

db.customers.insert({

username: "customer1",

email: "customer1@customer.com",

password: hex_md5("customer1paswd")

})

因为我们没有为_id字段指定一个值,所以它会自动生成一个唯一的ObjectId值。这个插入操作创建的文档是:

{

"_id" : ObjectId("5487ada1db4ff374fd6ae6f5"),

"username" : "customer1",

"email" : "customer1@customer.com",

"password" : "b1c5098d0c6074db325b0b9dddb068e1"

}

正如您在本节第一段中所看到的那样,插入接口并不是在MongoDB中创建新文档的唯一方法。通过在更新上使用upsert选项,我们也可以创建新文档。现在我们来进一步讨论这个问题。

3.2.2更新操作(update)

update接口用于修改MongoDB中以前存在的文档,甚至用于创建新文档。要选择我们想要更改的文档,我们将使用一个标准条件。更新可以修改文档字段值或整个文档。

更新操作一次只能修改一个文档。 如果标准化条件匹配多个文档,那么有必要将具有true值的多参数的文档传递给更新接口。如果条件没有文档与之匹配,且upsert参数为true时,则会创建一个新文档,否则它将更新匹配文档。

更新接口表示如下:

db.collection.update(

{

upsert: ,

multi: ,

writeConcern:

}

)

其中,

query是标准条件;

update是文档包含的要求改变的;

upsert是布尔值,为true表示无匹配文档时创建新文档;

multi是布尔值,为true表示更新匹配条件的每个文档。

WriteConcern是一个写入时关注域的文档的表达式。

使用前一部分中创建的文档,一个更新示例为:

db.customers.update(

,

{$set: }

)

修改后文档为:

{

"_id" : ObjectId("5487ada1db4ff374fd6ae6f5"),

"username" : "customer1",

"email" : "customer1@customer1.com",

"password" : "b1c5098d0c6074db325b0b9dddb068e1"

}

$set操作符允许我们只更新匹配文档的电子邮件字段。除此之外,可能会有这样更新:

db.customers.update(

,

)

此案例中,修改后的文档可能为:

{

"_id" : ObjectId("5487ada1db4ff374fd6ae6f5"),

"email" : "customer1@customer1.com"

}

也就是说,如果没有$set操作符,我们使用作为更新参数传递的文档来修改旧文档。 除了$set操作符外,我们还有其他重要的更新操作符:

• $incincrements the value of a field with the specified value:

• $inc用指定值递增字段的值

db.customers.update(

,

{$inc: {"details.age": 1}}

)

此更新将使匹配文档中的字段details.age增加1。

• $rename will rename the specified field:

• $rename重命名指定的字段

db.customers.update(

,

{$rename: }

)

此更新将在匹配的文档中把字段username重命名login。

• $unset willremove the field from the matched document:

• $unset将从匹配的文档中删除该字段

db.customers.update(

,

{$unset: }

)

此更新将从匹配的文档中删除login字段。

由于写入操作在单个文档的层次上是原子的,所以我们可以承受在使用前面的操作符时粗心。所有这些都可以安全使用。

3.2.3写入关注(Write Concerns)

围绕非关系数据库的许多讨论都与ACID概念有关。我们作为数据库专业人员、软件工程师、架构师和开发人员,都非常习惯于关系型的领域,我们花了很多时间开发而没有关心ACID问题。

尽管如此,我们现在应该明白为什么我们必须考虑这个问题,以及这些简单的四个字母在非关系世界中是如此重要。在本节中,我们将在MongoDB中讨论字母D意义,其意味着持久性。

持久性是数据库系统的一个属性,它告诉我们写入操作是否成功,事务是否已提交以及数据是否写入非易失性存储器中的持久介质(如硬盘)中。

与关系型数据库系统不同,NoSQL数据库中写入操作的响应由客户端决定。 再一次,我们有可能选择我们的数据建模,满足客户的特定需求。

在MongoDB中,成功写入操作的响应可以有很多级别的保证。 这就是我们所说的写入关注。水平从弱到强,由客户端决定保证的力度。对于我们来说,在同一个集合中,可能有一客户端需要强的写入关注,而另一位客户端需要较弱的关注。

MongoDB为我们提供的写入关注级别是:

•未确认

•已确认

•日志

•副本确认

3.2.3.1 未确认- Unacknowledged

顾名思义,在未确认写入的情况下,客户端将不会尝试响应写入操作。如果可能的,将只捕获网络错误。下图显示驱动程序不会等待MongoDB确认写入操作凭据:

在下面的示例中,我们在customers集合中执行了一个未确认写入关注的插入操作:

db.customers.insert(

,

}

)

3.2.3.2 已确认- Acknowledged

有了这个写入关注,客户端将会确认写入操作,并且能看到它写入了MongoDB的内存视图中。在这种模式下,客户端可以捕获网络错误和重复密钥。自2.6版本的MongoDB以来,这是默认的写入关注级别。

正如您前面所看到的,我们无法保证写入MongoDB的内存视图的会持久化到磁盘上。在MongoDB发生故障的情况下,内存视图中的数据将会丢失。下图显示了驱动程序等待MongoDB确认写入操作凭据,并将变化应用于数据的内存视图:

在以下示例中,我们在customers集合中插入了一个确认写入关注的操作:

db.customers.insert(

,

}

)

3.2.3.3 已日志- Journaled

有了日志记录的关注,客户端会收到确认日志中写入操作已提交的确认信息。因此,即使MongoDB出现问题,客户端也会保证数据将持久化到磁盘上。

为了减少使用日志写入问题时的延迟,MongoDB将其对日志的操作的频度从默认值100毫秒减少到30毫秒。下图显示驱动程序只有在将数据提交到日志后,才会等待MongoDB确认写入操作的凭据:

在以下示例中,我们在customers集合中执行了一个日志等级的写入关注插入操作:

db.customers.insert(

,

}

)

3.2.3.4 副本确认- Replicaacknowledged

在处理副本集时,务必确保写操作不仅在主节点中成功,而且还要传播到副本集的成员。为此目的,我们使用副本确认级的写入关注。

通过改变副本确认的默认写入关注点,我们可以确定我们希望写入操作确认的副本集的成员数。下图显示驱动程序将等待MongoDB确认在指定数量的副本集成员上接写入操作凭据:

在下面的例子中,我们将等待写操作传播到主节点和至少两个辅助节点:

db.customers.insert(

,

}

)

我们应该包含一个以毫秒为单位的超时属性,以避免写入操作在节点故障的情况下保持阻塞状态。

在下面的例子中,我们将等待,直到写入操作传播到主节点和至少两个辅助节点,超时时间为3秒。如果我们期望响应的两个辅助节点之一失败,则该方法在三秒钟后超时:

db.customers.insert(

,

}

)

3.2.4批量写入文件

有时插入、更新或删除集合中的多个记录非常有用。 MongoDB为我们提供了执行批量写入操作的功能。 批量操作适用于单个集合,集合可以是有序的或无序的。

与insert方法一样,有序的批量操作行为是连续处理记录,如果发生错误,MongoDB将返回而不处理任何剩余的操作。

无序操作的行为是并行处理的,所以如果发生错误,MongoDB仍然会处理剩下的操作。

我们还可以确定批量写入操作所需的确认级别。 从2.6版本开始,MongoDB引入了新的批量方法,我们可以使用它们插入、更新或删除文档。 但是,我们只能通过在insert方法上传递一组文档来进行批量插入。

在以下示例中,我们使用insert方法进行批量插入:

db.customers.insert(

[

,

,

]

)

在以下示例中,我们使用新的批量方法进行无序批量插入:

var bulk = db.customers.initializeUnorderedBulkOp();

bulk.insert();

bulk.insert();

bulk.insert();

bulk.execute();

我们应该使用MongoDB为我们提供的所有强大工具,但并非没有付出所有可能的关注。 MongoDB一次最多可以执行1000个批量操作。 所以,如果超过这个限制,MongoDB将把这些操作分成最多1000个批量操作的组。

本章总结

在本章中,您希望能够更好地理解MongoDB中的读写操作。此外,现在,您还应该理解为什么在文档建模过程之前已知需要执行的查询是很重要的。最后,您学习了如何在文档级别使用MongoDB属性(如原子性),并了解它如何帮助我们生成更好的查询。

在下一章中,您将看到一种称为索引的特殊数据结构如何改进查询的执行。

本书的第三章: 查询文档 到此为止,敬请期待第四章的到来……

在这里,老崔还是希望您能关注本号,点个赞、分享或收藏一下,再次感谢^_^

码字很辛苦,打赏点呗^_^

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180524A0RCP600?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券