大数据的挑战与机遇

一、大数据概述

大数据是指具有大量数据、多样结构和高速生成的数据。这种数据形式对传统的关系型数据库系统在存储和处理数据方面提出了挑战。大数据为处理和存储数据提供了新的方法。在本章中,我们将讨论大数据的基础知识、来源和挑战,以及大数据的三个关键特性:容量、速度和多样性。此外,我们还将探讨传统技术在处理大数据时所面临的限制。

1.1 大数据入门

随着云计算、社交网络、分析和移动设备的普及,大数据已经成为当今信息技术领域的热门话题。互联网和电子设备的普及使得智能手机、社交网站、平板电脑和其他数据生成设备能够产生大量的数据。这些数据以各种格式生成,如视频、文本、语音、日志文件和图像等。例如,一秒钟的高清视频产生的字节数是一页文本的2000倍。

以脸书公司网站上的统计数据为例:2015年6月,脸书网站上有9.68亿活跃用户;2015年6月,移动端有8.44亿活跃用户;截至2015年6月30日,月活跃用户达到14.9亿;截至2015年6月30日,移动端月活跃用户达到13.1亿;自2013年5月起,每天产生的点赞数达到45亿,比2012年8月增长了67%。

图1-1展示了Twitter的统计数据。

二、大数据的挑战与机遇

大数据带来了许多挑战,但同时也为我们提供了巨大的机遇。以下是大数据的一些挑战及其解决方案:

1. 数据的存储和管理:随着数据量的不断增加,如何有效地存储和管理这些数据成为了一个重要的问题。解决方案包括使用分布式文件系统(如Hadoop HDFS)和NoSQL数据库(如Cassandra和MongoDB)等技术来实现数据的分布式存储和管理。

2. 数据的处理和分析:大数据的特点是数据量大、速度快和结构多样。因此,如何快速地处理和分析这些数据成为一个关键问题。解决方案包括使用批处理框架(如Spark)和流处理框架(如Flink)等技术来实现高效的数据处理和分析。

3. 数据的安全和隐私保护:随着大数据应用的普及,如何在保障数据安全的同时保护用户隐私成为一个重要议题。解决方案包括使用加密技术(如SSL/TLS)和访问控制技术(如OAuth)等手段来确保数据的安全性和用户隐私的保护。

总之,大数据给我们带来了诸多挑战,但同时也为我们提供了巨大的机遇。通过采用合适的技术和方法,我们可以充分利用大数据的价值,推动各行各业的发展。

大数据是指无法在一定时间内用常规软件工具对其内容进行抓取、管理和发展的数据集合。大数据类型因行业而异,包括结构化数据、非结构化数据和半结构化数据 。其中,结构化数据最容易整理和搜索,主要包括财务数据、机器日志和人口统计明细等;而非结构化数据则包括文本、图片、音频和视频等 。半结构化数据则介于两者之间,例如电子邮件。

. 大数据类型与行业分布

MGI的研究揭示了数据类型的多样性及其在不同行业中的分布。不同行业产生的数据类型各异,例如零售和批发、政府行政部门以及金融服务等行业产生大量的文本和数字数据,包括客户数据、事务信息以及数学建模和模拟。而制造业、医疗保健、媒体和通信等行业则负责更高比例的多媒体数据。X射线、CT和其他扫描形式的图像数据在医疗保健的数据存储量中占据主导地位。

2. 大数据地理分布

在全球范围内,北美和欧洲目前拥有大数据总量的70%。由于云计算的发展,一个地区产生的数据可以存储在另一个国家的数据中心。因此,拥有大量云和托管提供商产品的国家往往拥有大量数据存储。

3. 大数据的主要来源

本节将讨论导致数据不断增长的主要因素。MGI报告指出,企业现在以更精细的方式收集数据,为每笔事务附加更多细节,以了解消费者行为。此外,医疗保健、产品公司等行业中多媒体使用的增加;社交媒体网站如脸书、推特等越来越受欢迎;智能手机的快速普及,使用户能够积极使用社交媒体网站和其他互联网应用;以及日常生活中通过网络连接到计算资源的传感器和设备的使用越来越多。MGI报告还预测,未来五年,传感器等机器对机器设备(也被称为物联网)的数量将以每年超过30%的速度增长。

4. 数据生成模型的变化

随着消费IT和互联网技术的渗透,以及社交媒体等趋势的发展,数据生成模式已经从少数公司生成数据,其他公司消费数据的模式转变为人人生成数据,人人消费数据的模式。图1-4描述了数据生成模型的变化。

5. 大数据的三个V

我们将大数据定义为具有三个V的数据:量、速度和多样性。组织和IT领导者必须关注这三个方面。

6. 大数据的三个V详解

6.1 体积

大数据中的体积意味着数据的大小。各种因素影响着大数据的规模:随着业务变得越来越以事务为导向,我们看到事务数量不断增加;越来越多的设备连接到互联网,这增加了容量;互联网的使用越来越多;内容的数字化也在增加。图1-6描绘了自2009年以来数字世界的增长。

数据量的增长

在现代场景中,数据并不仅仅是来自企业内部产生的;它还基于与扩展企业和客户的事务而生成。这要求企业对客户数据进行大量维护。目前,十亿字节的规模变得越来越普遍。图1-7描绘了数据增长率。如此庞大的数据量是大数据技术面临的最大挑战。以及时且经济的方式存储、处理和访问数据所需的存储和处理能力是巨大的。

1.4.2 数据的多样性

从各种设备和来源生成的数据没有固定的格式或结构。与文本相比,CSV或RDBMS数据不同于文本文件、日志文件、流视频、照片、仪表读数、股票行情数据、pdf、音频和各种其他非结构化格式。如今,数据的结构无法控制。新的数据源和数据结构正在快速创建。因此,技术上的责任是找到一种解决方案来分析和可视化存在的大量数据。例如,要为通勤者提供备用路线,交通分析应用需要来自数百万部智能手机和传感器的数据,以提供对交通状况和备用路线的准确分析。

1.4.3 大数据的速度

大数据中的速度是数据创建的速度和处理数据的速度。如果数据不能以要求的速度处理,它就失去了意义。由于来自社交媒体网站、传感器、报价器、计量和监控的数据流,无论数据是动态的还是静态的,组织都必须快速处理数据(参见图1-8)。对于大数据技术来说,以足够快的速度应对和处理数据是另一个挑战。

图1-8。

大数据的三个方面

在许多大数据用例中,实时洞察至关重要。例如,算法事务系统从市场和Twitter等社交媒体网站获取实时信息,以做出股票事务决策。处理这些数据的任何延迟都可能意味着损失数百万美元的股票事务机会。每当讨论大数据时,都会谈到第四个V。第四个V是准确性,这意味着并不是所有的数据都是重要的,所以确定什么将提供有意义的洞察力,什么应该被忽略是至关重要的。

1.5 大数据的应用

本节将重点介绍使用大数据为组织创造价值的方法。在我们深入研究如何让大数据为组织所用之前,我们先来看看大数据为什么重要。大数据是一个全新的数据来源;它是当你在博客上发表文章时产生的数据,比如一个产品或旅行。以前,这种细微的可用信息没有被捕获。现在,it和采用此类数据的组织可以追求创新、提高灵活性并增加盈利能力。

大数据可以为任何组织创造价值,这可以大致分为五种使用大数据的方式。第一种是能见度,即相关利益相关者及时访问数据会产生巨大的价值。第二种是发现和分析信息,大数据的大部分价值来自于从外部来源收集的数据可以与组织的内部数据合并。第三种是细分和定制,大数据使组织能够创建量身定制的产品和服务,以满足特定细分市场的需求。第四种是辅助决策,大数据可以极大地降低风险,改善决策,并揭示有价值的见解。第五种是创新,大数据以产品和服务的形式实现了新思想的创新。

此外,大数据也带来了一些挑战。随着越来越多的数据在全球范围内被收集、数字化和移动,策略和法规遵从性问题变得越来越重要。数据隐私、安全、知识产权和保护对组织来说非常重要。遵守各种法令和法律要求给数据处理带来了挑战。围绕数据的所有权和责任问题是大数据案例中需要处理的重要法律问题。

大数据项目的合规性挑战主要涉及数据所有权、数据合理使用和数据保密性责任等政策问题。此外,访问供消费的数据也是一个挑战,因为第三方可能会获取这些数据,而获取这些数据可能面临法律和合同方面的挑战。为了解决这些问题,需要将访问大数据的合同条款和经济激励捆绑在一起,以使消费者能够获得数据。

在技术和工艺方面,必须利用专门为满足大数据需求而构建的新工具和技术,而不是试图通过遗留系统来解决上述问题。一方面,遗留系统在处理大数据方面的不足,另一方面,新技术中缺乏经验丰富的资源,这是任何大数据项目都必须应对的挑战。

在本节中,我们将讨论组织在使用传统系统管理大数据时面临的挑战。由于大数据是有很多结构的数据(如图像、视频、日志等),而传统系统设计用于处理结构化数据(其中定义了带有列的表),因此无法处理或保存大数据。此外,在存储、计算和检索领域部署更新的技术以及分析新数据也是必要的。

大数据处理是指处理海量数据的技术和方法。随着数据量的不断增加,越来越多的技术正在利用这些技术进步。在《NoSQL数据库实战》一书中,我们将讨论MongoDB,这是一种可用于存储和处理大数据的技术。

在本章中,您了解了大数据。您研究了产生大数据的各种来源,以及大数据的用途和带来的挑战。您还了解了为什么需要更新的技术来存储和处理大数据。在接下来的章节中,您将了解一些有助于组织管理大数据并使他们能够从大数据中获得有意义的见解的技术。

NoSQL是一种设计互联网规模的数据库解决方案的新方法。它不是一种产品或技术,而是一个术语,它定义了一组不是基于传统RDBMS原则的数据库技术。在这一章,我们将涵盖NoSQL的定义和基础知识。我们将向你介绍CAP定理,并讨论NRW符号。我们将比较ACID和BASE方法,并通过比较NoSQL和SQL数据库技术来结束本章。

在本章中,我们将介绍RDBMS系统。RDBMS系统非常适合存储在列和行中的结构化数据,可以使用SQL查询这些数据。RDBMS系统基于ACID事务的概念。ACID代表原子性、一致性、孤立性和持久性,其中原子意味着要么完全应用事务的所有更改,要么根本不应用。一致意味着数据在应用事务后处于一致状态。这意味着提交事务后,获取特定数据的查询将看到相同的结果。隔离意味着应用于同一组数据的事务相互独立。因此,一个事务不会干扰另一个事务。持久意味着更改在系统中是永久性的,即使出现任何故障也不会丢失。

NoSQL 是一个用来指代非关系型数据库的术语,它主要包含了大部分不依赖传统关系型数据库管理系统(RDBMS)的原则的数据存储技术,并且适用于处理互联网规模的大型数据集。正如上一章所讨论的,大数据对传统的数据存储和处理方式(如 RDBMS 系统)提出了挑战。因此,我们看到了 NoSQL 数据库的兴起,这种数据库的设计是为了在时间和成本的限制下处理如此巨大数量和种类的数据。

NoSQL 数据库是从处理大数据的需求发展而来的;传统的关系型数据库技术不能提供足够的解决方案。图 2-1 显示了与结构化数据相比,这些年来非/半结构化数据的增长。

Structured vs. un/Semi-Structured data

下面是一些非常适合 NoSQL 数据库的大数据用例示例:

社交网络图:谁和谁有联系?谁的帖子应该出现在社交网站的用户墙或主页上?

搜索和检索:用特定的关键字搜索所有相关的页面,按关键字在页面上出现的次数排序。

2.2.1 定义

NoSQL 没有正式的定义。它代表了一种与 RDBMS 根本不同的持久性/数据存储机制。但是如果硬要定义 NoSQL,这里就是:NoSQL 是不遵循 RDBMS 原则的数据存储的总称。

Note

该术语最初用于表示“如果您想要伸缩,就不要使用 SQL”,后来,这被重新定义为“不仅仅是 SQL”,这意味着除了 SQL 之外,还存在其他补充的数据库解决方案。

2.2.2 NoSQL 简史

1998 年,Carlo Strozzi 创造了术语 NoSQL。他用这个术语来标识他的数据库,因为数据库没有 SQL 接口。这个术语在 2009 年初再次出现,当时 Eric Evans(Rackspace 的一名员工)在一次关于开源分布式数据库的活动中使用这个术语来指代非关系型的分布式数据库,并且没有遵循关系型数据库的 ACID 特性。

2.3 酸与碱

在介绍中,我们提到了传统的 RDBMS 应用关注于 ACID 事务。无论这些品质看起来多么重要,它们都与 Web 规模的应用的可用性和性能要求不相容。比方说,你有一家像 OLX 这样的公司,销售诸如未使用的家庭用品(旧家具、车辆等)之类的产品,并使用 RDBMS 作为其数据库。让我们考虑两种情况:

1. 如果你想扩展你的业务并增加更多的用户访问量,你会发现使用关系型数据库可能会面临性能瓶颈。而 NoSQL 数据库可以更好地支持横向扩展,以应对不断增长的用户需求。

2. 如果你需要实时分析大量的用户行为数据,那么关系型数据库可能无法满足实时查询的需求。而 NoSQL 数据库通常具有更高的读写性能,可以更快速地处理大量数据。

第一个场景:让我们看一个电子商务购物网站,用户正在购买产品。在事务过程中,用户锁定数据库的一部分,即库存,其他用户必须等待,直到锁定的用户完成事务。

第二种情况:应用可能最终使用缓存的数据,甚至未锁定的记录,导致不一致。在这种情况下,当库存实际为零时,两个用户可能最终购买了该产品。系统可能会变慢,影响可扩展性和用户体验。与传统 RDBMS 系统的 ACID 方法相反,NoSQL 使用一种通常称为 BASE 的方法来解决这个问题。在解释 BASE 之前,我们先来探讨一下 CAP 定理的概念。

CAP 定理指出,在任何时间点,分布式系统只能满足上述三个保证中的两个(图 2-2)。图 2-2。基本可用意味着系统在 CAP 定理中是可用的。软状态表示即使没有输入提供给系统,状态也会随着时间而改变。这符合最终的一致性。最终一致性意味着系统将在长期内达到一致性,前提是在此期间没有输入被发送到系统。因此,BASE 与 RDBMS ACID 事务相反。您已经看到 NoSQL 数据库最终是一致的,但是不同的 NoSQL 数据库最终的一致性实现可能会有所不同。NRW 是用于描述最终一致性模型如何在 NoSQL 数据库中实现的符号,其中 n 是数据库维护的数据副本的数量。r 是应用在返回读取请求的输出之前需要引用的副本数。w 是在将写操作标记为成功完成之前需要写入的数据副本的数量。使用这些符号配置,数据库实现了最终一致性的模型。可以在读取和写入操作级别实现一致性。写操作

NoSQL 数据库是一种非关系型数据库,具有分布式架构、高性能和灵活性等优势,适用于大规模互联网应用、非结构化数据存储和实时分析等多种场景。

在优势方面,主要体现在以下三点:

1. 简单的扩展:NoSQL 数据库不需要预先定义表结构,可以根据实际需求动态调整数据模型。这种灵活性使得 NoSQL 数据库适用于存储各种类型的数据,包括结构化数据、半结构化数据和非结构化数据 。

2. 高性能:NoSQL 数据库通常被设计成与廉价的商用服务器集群一起工作,使用户能够以较低的成本存储和处理更多的数据。此外,NoSQL 数据库通常具有更好的可扩展性和更好的性能 。

3. 高可用性:NoSQL 数据库通常具有更高的可用性和更好的故障转移能力。例如,MongoDB 通过复制集来实现高可用性。

在劣势方面,主要体现在以下两个方面:

1. 不支持 ACID 事务:ACID 是原子性、一致性、隔离性和持久性的缩写,是关系型数据库所采用的标准。但是,由于 NoSQL 数据库不支持 ACID 事务,因此在保证数据的一致性方面存在一定的困难。

2. 可管理性较差:由于 NoSQL 数据库主要用于自动修复、分布式数据和更简单的数据模型,导致可管理性和管理性较低。

成熟度:大多数 NoSQL 数据库都是预生产版本,其关键特性仍有待实现。因此,在决定使用 NoSQL 数据库时,您应该对产品进行适当的分析,以确保这些特性被完全实现,而不是仍然在待办事项列表中。

支持:支持是你需要考虑的一个限制。大多数 NoSQL 数据库来自开源的初创企业。因此,与企业软件公司相比,支持是非常少的,并且可能没有全球影响力或支持资源。

有限的查询能力:由于 NoSQL 数据库通常是为了满足 web 级应用的伸缩需求而开发的,所以它们提供的查询能力有限。一个简单的查询需求可能涉及大量的编程专业知识。

管理:尽管 NoSQL 旨在提供一个无管理的解决方案,但它仍然需要技巧和精力来安装和维护该解决方案。

专业知识:由于 NoSQL 是一个不断发展的地区,开发者和管理员社区对该技术的专业知识非常有限。

虽然 NoSQL 正在成为数据库领域的重要组成部分,但是您需要了解这些产品的局限性和优势,以便正确选择 NoSQL 数据库平台。

2.5 SQL 与 NoSQL 数据库

现在您已经了解了关于 NoSQL 数据库的细节。尽管 NoSQL 越来越多地被用作数据库解决方案,但它并不是要取代 SQL 或 RDBMS 数据库。在这一节中,您将看到 SQL 和 NoSQL 数据库之间的差异。

让我们快速回顾一下 RDBMS 系统。RDBMS 系统已经流行了大约 30 年,甚至现在它们还是应用数据存储解决方案架构师的默认选择。如果我们要列出 RDBMS 系统的几个优点,首先也是最重要的是 SQL 的使用,它是一种用于数据处理的丰富的声明式查询语言。很好的被用户理解。此外,RDBMS 系统提供了对事务的 ACID 支持,这在许多领域是必不可少的,例如银行应用。

然而,RDBMS 系统的最大缺点是,随着数据的增加,它很难处理模式变化和伸缩问题。随着数据的增加,读/写性能会下降。您面临 RDBMS 系统的伸缩问题,因为它们主要是为纵向扩展而不是横向扩展而设计的。

与 SQL RDBMS 数据库相反,NoSQL 提倡脱离 RDBMS 范式的数据存储。让我们讨论一下技术场景,以及它们在 RDBMS 和 NoSQL 中的比较:http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/

表2-2比较了SQL和NoSQL技术。

| 类型 | SQL数据库 | NoSQL数据库 | | --- | --- | --- | |

| :---: | :---: | :---: |

| 发展历史 | 开发于1970年 | 开发于2000年代 |

| 例子 | SQL Server、Oracle、MySQL。 | 蒙戈布,巴塞,卡珊德拉。 |

| 数据存储模型 | 数据存储在表的行和列中,其中每一列都有特定的类型。这些表格通常是根据标准化原则创建的。联接用于从多个表中检索数据。 | 数据模型取决于数据库类型。比方说,数据存储为键值存储的键值对。在基于文档的数据库中,数据存储为文档。与RDBMS的僵硬的表模型相比,数据模型是灵活的。 |

| 计划 | 固定的结构和模式,因此对模式的任何更改都会涉及到数据库的更改。 | 动态模式、新的数据类型或结构可以通过扩展或改变当前模式来适应。可以动态添加新字段。 |

| 可量测性 | 使用放大方法;这意味着随着负载的增加,需要购买更大、更贵的服务器来容纳数据。 | 使用横向扩展方法;这意味着将数据负载分布在廉价的商用服务器上。 |

| 支持事务 | 支持ACID和事务。 | 支持分区和可用性,以及对事务的妥协。事务存在于特定的级别,例如数据库级别或文档级别。 |

| 一致性 | 一致性强。 | 取决于产品。很少有人选择提供强一致性,而很少有人提供最终一致性。 |

| 支持 | 提供高水平的企业支持。 | 开源模式。通过构建开源产品的第三方或公司提供支持。 |

| 成熟度 | 已经存在很久了。 | 有的是成熟的;其他的在进化。 |

| 查询功能 | 通过易于使用的GUI界面提供。 | 查询可能需要编程技能和知识。而不是一个用户界面,重点是功能和编程接口。 |

| 专家意见 | 利用SQL语言和RDBMS概念来设计和开发应用的大型开发者社区。 | 开发这些开源工具的小型开发者社区。 |

表2-3展示了NoSQL领域的一些项目及其所属类别及参与者。

NoSQL 数据库根据数据的存储方式进行分类。NoSQL 大多遵循水平结构,因为需要提供大量精选信息,通常是近实时的。它们针对大规模的插入和检索操作进行了优化,内置了复制和集群功能。

表2-4中列出了不同类别的NoSQL数据库之间的特性比较。在考虑NoSQL项目时,重要的是您感兴趣的特性集。当决定使用NoSQL的产品时,首先你需要非常仔细地理解问题需求,然后你应该看看其他已经使用NoSQL产品解决类似问题的人。请记住,NoSQL仍在不断成熟,因此这将使您能够从同行和以前的部署中学习,并做出更好的选择。

此外,你还需要考虑以下问题:需要处理的数据有多大?读写的吞吐量是多少?如何在系统中实现一致性?系统需要支持高写性能还是高读性能?可维护性和管理性有多容易?

你需要查询什么?

使用 NoSQL 的好处是什么?

我们建议您从小处着手,但意义重大,并尽可能考虑混合方法。

2.7 摘要

在这一章中,你了解了 NoSQL。您现在应该明白什么是 NoSQL,以及它与 SQL 有何不同。你还研究了 NoSQL 的各种类别。

在接下来的章节中,您将了解 MongoDB,这是一个基于文档的 NoSQL 数据库。

三、MongoDB 简介

"MongoDB is one of NoSQL’s leading document storage databases. It enables organizations to process big data and gain meaningful insights from it. "

一些领先的企业和消费者 IT 公司已经在其产品和解决方案中利用了 MongoDB 的功能。MongoDB 3.0 版本引入了可插拔存储引擎和 Ops Manager,这扩展了最适合 MongoDB 的应用集。

MongoDB 的名字来源于单词“humungous”。像其他 NoSQL 数据库一样,MongoDB 也不符合 RDBMS 原则。它没有表、行和列的概念。此外,它不提供 ACID 遵从性、连接、外键等特性。

MongoDB 将数据存储为二进制 JSON 文档(也称为 BSON)。文档可以有不同的模式,这意味着模式可以随着应用的发展而改变。MongoDB 是为可伸缩性、性能和高可用性而构建的。

在这一章中,我们将讨论一下 MongoDB 的创建和设计决策。我们将在接下来的章节中研究 MongoDB 的关键特性、组件和架构。

3.1 历史

2007 年下半年,Dwight Merriman、Eliot Horowitz 和他们的团队决定开发一个在线服务。该服务的目的是为开发、托管和自动扩展 web 应用提供一个平台,这与 Google App Engine 或 Microsoft Azure 等产品非常相似。很快他们意识到没有开源数据库平台适合这项服务的需求。

MongoDB的设计理念是为了应对大数据量、高性能和灵活性需求。在设计 MongoDB 时,设计团队的目标是创建一个快速、大规模可伸缩且易于使用的数据库。为了在分区数据库中实现速度和水平可伸缩性,正如 CAP 定理中所解释的,一致性和事务支持必须折衷。因此,根据这个定理,MongoDB 以一致性和事务支持为代价提供了高可用性、可伸缩性和分区。实际上,这意味着 MongoDB 使用文档而不是表和行来使其灵活、可伸缩和快速。

MongoDB的一个很重要的设计理念是:服务端只关注底层核心能力的输出,至于怎么用,就尽可能地将工作交给客户端去决策。这也就是 MongoDB 灵活性的保证,但是灵活性带来的代价就是使用成本的提升。

MongoDB 团队决定采用非关系方法来解决这些问题。如前所述,MongoDB 将其数据存储在 BSON 文档中,所有相关数据都放在一起,这意味着一切都在一个地方。MongoDB 中的查询基于文档中的键,因此文档可以分布在多个服务器上。查询每个服务器意味着它将检查自己的文档集并返回结果。这实现了线性可伸缩性和改进的性能。

MongoDB 有一个主-从复制,主服务器接受写请求。如果写性能需要提高,那么可以使用分片;这将数据分割到多台机器上,使这些机器能够更新数据集的不同部分。在 MongoDB 中分片是自动的;随着机器数量的增加,数据会自动分发。

基于 JSON 的文档存储

MongoDB 使用基于 JSON(JavaScript 对象表示法)的文档存储来存储数据。JSON/BSON 提供了一个无模式模型,在数据库设计方面提供了灵活性。与 RDBMSs 不同,可以无缝地对模式进行更改。

这种设计还通过在内部将相关数据分组在一起并使其易于搜索来实现高性能。

JSON 文档包含实际数据,相当于 SQL 中的一行。然而,与 RDBMS 行相反,文档可以有动态模式。这意味着集合中的文档可以有不同的字段或结构,或者公共字段可以有不同类型的数据。

文档包含键值对形式的数据。让我们用一个例子来理解这一点:

```json

{

"Name": "ABC",

"Phone": ["1111111", ...],

"Fax": ...

}

```

如上所述,键和值是成对出现的。文档中的键值可以留空。在上面的例子中,文档有三个键,即“姓名”、“电话”和“传真”“传真”键没有价值。

性能与功能对比

为了让 MongoDB 高性能、快速度,RDBMS 系统中常见的某些特性在 MongoDB 中是没有的。MongoDB 是一个面向文档的 DBMS,其中数据存储为文档。它不支持连接,也没有完全一般化的事务。但是,它确实提供了对二级索引的支持,它使用户能够使用查询文档进行查询,并且它提供了对每个文档级别的原子更新的支持。它提供了一个副本集,这是一种具有自动故障转移的主从复制形式,并且它具有内置的水平伸缩功能。

MongoDB 和 SQL 的主要区别在于它们的数据库架构。MySQL 是一个关系型数据库,它将数据存储在由表、行和列组成的二维表格中。每个表都有一个主键,可以用来唯一标识它的每一行。表之间可以通过外键来建立关联。MongoDB 是非关系型数据库,它将数据存储在由文档组成的集合中。每个文档都是一个键值对的集合,可以包含各种不同的数据类型。文档之间可以通过嵌入或者引用来建立关联 。

MongoDB, a popular NoSQL database, uses documents as the primary means of storing data. One of its key features is the flexible schema it offers, where documents within the same collection can have varying fields. This feature enables users to store complex datatypes such as nested or multi-value fields like arrays and hashes. In contrast, Relational Database Management Systems (RDBMSs) enforce fixed schemas where columns must have similar data types, and arrays or nested values are not typically supported.

While MongoDB doesn't support JOIN operations natively like SQL databases, it allows users to organize all relevant data in a single document, thus reducing the need for JOIN operations. However, this does come with a workaround that we will explore in more detail later on. Another significant difference between MongoDB and SQL databases is their approach to transactions. MongoDB doesn't support transactions in the same way as SQL databases; however, it ensures atomicity at the document level. It also employs an isolation operator to isolate write operations that involve multiple documents, but it doesn't provide "all-or-nothing" atomicity for multi-document write operations. These nuances make MongoDB a versatile choice for certain applications, but understanding them is essential when designing a database schema or working with queries in MongoDB.

MongoDB 数据模型

MongoDB 是一个基于文档的数据库系统,其中的文档可以有一个灵活的模式。这意味着集合中的文档可以有不同(或相同)的字段集。这为您处理数据提供了更大的灵活性。在本章中,您将探索 MongoDB 灵活的数据模型。在任何需要的地方,我们将展示这种方法与关系数据库管理系统(RDBMS)系统的不同之处。

一个 MongoDB 部署可以有许多数据库。每个数据库都是一组集合。集合类似于 SQL 中的表的概念;然而,它们是无模式的。每个集合可以有多个文档。将文档想象成 SQL 中的一行。本图展示了 MongoDB 数据库模型。

在关系型数据库系统中,由于表结构和每列的数据类型是固定的,所以只能在一列中添加特定数据类型的数据。而在 MongoDB 中,集合是文档的集合,其中数据存储为键值对。

让我们通过一个例子来理解数据是如何存储在文档中的。以下文档包含用户的姓名和电话号码:

```json

{

"Name": "ABC",

"Phone": ["1111111", "222222"]

}

```

动态模式意味着同一集合中的文档可以有相同或不同的字段或结构集,甚至公共字段也可以跨文档存储不同类型的值。在集合的文档中存储数据的方式没有严格的限制。

让我们看一个区域集合的例子:

```json

{

"R_ID": "REG001",

"Name": "United States"

}

```

MongoDB 是一个基于文档的数据库,它使用二进制 JSON(BSON)来存储数据。在本节中,我们将了解 JSON 和 BSON。JSON 代表 JavaScript 对象符号,它是当今现代网络中用于数据交换的标准(和 XML 一起)。该格式是人和机器可读的,这不仅是交换数据的好方法,也是存储数据的好方法。

JSON 支持所有基本的数据类型,如字符串、数字、布尔值和数组。以下代码显示了 JSON 文档的样子:

```json

{

"_id": 1,

"name": {

"first": "John",

"last": "Doe"

},

"publications": [

{

"title": "First Book",

"year": 1989,

"publisher": "publisher1"

},

{

"title": "Second Book",

"year": 1999,

"publisher": "publisher2"

}

]

}

```

JSON 允许您将所有相关的信息保存在一个地方,这提供了出色的性能。它还使文档的更新变得独立。这是无模式的。而 BSON 是 MongoDB 使用的二进制 JSON 格式,它在某些情况下可以提供更好的性能。

MongoDB 使用二进制编码格式存储 JSON 文档,称为 BSON。BSON 是 JSON 数据模型的扩展形式。MongoDB 对 BSON 文档的实现具有快速、高度可穿越和轻量级的特点。它支持在其他数组中嵌入数组和对象,以及在对象内部构建索引,根据查询表达式匹配对象,包括顶级和嵌套的 BSON 键。

4.1.2 标识符(_id)

您已经了解 MongoDB 将数据存储在文档中,这些文档由键值对组成。尽管文档可以类比为关系型数据库中的行,但它们与行不同,因为文档具有灵活的模式。键只是一个标签,可以粗略地类比为关系型数据库中的列名。密钥用于从文档中查询数据。因此,就像关系型数据库主键(用于唯一标识每一行)一样,您需要有一个唯一标识集合中每个文档的键。这在 MongoDB 中被称为 _id。如果您没有为一个键显式指定任何值,MongoDB 将自动生成一个唯一的值并分配给它。这个键值是不可变的,可以是除数组之外的任何数据类型。

4.1.3 加盖收藏

您现在已经非常熟悉集合和文档了。现在我们来谈谈一种特殊类型的集合,叫做封顶集合。MongoDB 有一个集合封顶的概念,意味着它按照插入的顺序存储集合中的文档。当集合达到其限制时,文档将按 FIFO(先进先出)顺序从集合中移除。这意味着最近最少插入的文档将首先被删除。这对于需要自动维护插入顺序以及需要在固定大小后删除记录的用例来说很好。一个这样的用例是在达到一定大小后自动截断的日志文件。注意:MongoDB 本身使用上限集合来维护其复制日志。Capped 集合保证了按插入顺序保存数据,因此按插入顺序检索数据的查询可以快速返回结果,并且不需要索引。不允许更改文档大小的更新。

4.2 多态模式

既然您已经熟悉了 MongoDB 数据结构的无模式特性,现在让我们来探索多态模式和用例。多态模式是一种模式,其中集合具有不同类型或模式的文档。这种模式的一个很好的例子是名为 Users 的集合。一些用户文档可能有额外的传真号码或电子邮件地址,而其他用户文档可能只有电话号码,然而所有这些文档都共存于同一个用户集合中。这种模式通常被称为多态模式。在本章的这一部分,你将探究使用多态模式的各种原因。面向对象的编程

面向对象编程(OOP)允许您使用继承让类共享数据和行为。它还允许您在父类中定义可以在子类中覆盖的函数,从而在不同的上下文中以不同的方式运行。换句话说,您可以使用相同的函数名来操作子类和父类,尽管在底层实现可能会有所不同。这种特性被称为多态性。

在这种情况下,要求能够拥有一个模式,其中所有相关的对象集或层次结构中的对象集可以放在一起,并且还可以进行相同的检索。让我们考虑一个例子。假设您有一个应用,允许用户上传和共享不同的内容类型,如 HTML 页面、文档、图像、视频等。尽管许多字段在上述所有内容类型中都是通用的(如姓名、ID、作者、上传日期和时间),但并非所有字段都是相同的。例如,对于图像,您有一个保存图像内容的二进制字段,而 HTML 页面有一个保存 HTML 内容的大文本字段。

在这个场景中,可以使用 MongoDB 多态模式,其中所有的内容节点类型都存储在同一个集合中,比如 LoadContent,每个文档只有相关的字段。

```javascript

// "Document collections" - "HTMLPage" document

{

_id: 1,

title: "Hello",

type: "HTMLpage",

text: "Hi..Welcome to my world"

}

...

// Document collection also has a "Picture" document

{

_id: 3,

title: "Family Photo",

type: "JPEG",

sizeInMB: 10,........

}

```

这种模式不仅使您能够将不同结构的相关数据存储在同一个集合中,还简化了查询。同一个集合可用于对常见字段执行查询,例如获取在特定日期和时间上传的所有内容,以及对特定字段执行查询,例如查找大小大于 X MB 的图像。因此,面向对象编程是具有多态模式有意义的用例之一。

模式演变是当您使用数据库时需要考虑的最重要的事项之一。设计应该以对应用影响最小或没有影响的方式进行,也就是说没有或只有很少的停机时间,没有或只有很少的代码更改等。

通常,模式演变是通过执行迁移脚本将数据库模式从旧版本升级到新版本来实现的。如果数据库不在生产环境中,脚本可以简单地删除并重新创建数据库。但是,如果数据库在生产环境中,并且包含实时数据,迁移脚本将会很复杂,因为需要保留数据。剧本应该考虑到这一点。虽然 MongoDB 提供了一个更新选项,如果添加了一个新的字段,可以使用它来更新集合中所有文档的结构,但是想象一下,如果集合中有数千个文档,那么这样做会产生什么影响。它会非常慢,并且会对底层应用的性能产生负面影响。其中一种方法是将新的结构包含到添加到集合中的新文档中,然后在应用仍在运行时逐渐在后台迁移集合。这是拥有多态模式将带来优势的众多用例之一。

例如,假设您正在处理一个票据集合,其中有包含票据详细信息的文档,如下所示:

```json

// "Ticket1" document (stored in "Tickets" collection)

{

_id: 1,

Priority: "High",

type: "Incident",

text: "Printer not working"

}

```

在某个时候,应用团队决定在票据文档结构中引入一个“简短描述”字段,所以最好的替代方法是在新的票据文档中引入这个新字段。在应用中,嵌入一段代码来处理检索“旧样式”文档(没有简短描述字段)和“新样式”文档(有简短描述字段)。旧样式文档可以逐渐迁移到新样式文档。迁移完成后,如果需要,可以更新代码以删除嵌入的用于处理缺失字段的代码。

在本章中,您学习了 MongoDB 数据模型。您还了解了标识符和上限集合。在本章结束时,您已经理解了灵活的模式是如何帮助您的。在下一章中,您将开始使用 MongoDB。您将执行 MongoDB 的安装和配置。五、MongoDB 安装和配置 “MongoDB is a cross-platform database.” 在本章中,您将了解在 Windows 和 Linux 上安装 MongoDB 的过程。5.1 选择您的版本 www.mongodb.org/downloads

MongoDB支持32位和64位架构,但是建议在生产环境中使用64位架构。这是因为在MongoDB中使用了内存映射文件,这将32位版本的数据限制在2GB左右。如果您的系统是64位的,那么您可以使用64位版本的MongoDB来避免这个问题。

如果您需要在Linux上安装MongoDB,可以参考官方文档。如果您需要在Windows上安装MongoDB,可以在官方网站下载32位或64位的.msi文件,然后按照操作提示进行安装。

以下是根据您提供的内容重构的段落结构:

1. 使用GPG和APT安装MongoDB:

```bash

public.GPGsudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10/etc/apt/sources.list.d/mongodb-org-3.0.listecho "debhttp://repo.mongodb.org/apt/ubuntu"$(lsb_release -sc)"/mongodb-org/3.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.0.listsudo apt-get updatesudo apt-get install -y mongodb-org

```

您已经成功安装了 MongoDB,这就是它的全部内容。

2. 手动安装MongoDB:

在本节中,您将看到如何手动安装 MongoDB。这一知识在以下情况下很重要:

- 当 Linux 发行版不使用 Aptitude 时。

- 当您需要的版本无法通过存储库获得或者不属于存储库时。

- 当你需要同时运行多个 MongoDB 版本时。

手动安装的第一步是决定要使用的 MongoDB 版本,然后从站点下载。接下来,需要使用以下命令提取包:

```bash

$ tar -xvf mongodb-linux-x86_64-3.0.4.tgz

```

这就成功完成了 MongoDB 的安装。

在 Windows 上安装 MongoDB 很简单,只需下载所选 Windows 版本的 msi 文件并运行安装程序。

安装程序将引导您完成 MongoDB 的安装。按照向导,您将到达选择安装类型屏幕。有两种安装类型可供您自定义安装。在本例中,选择“自定义”安装类型。

C:\PracticalMongoDB

C:\Program FilesMongoDB

单击“下一步”将带您进入“准备安装”屏幕。单击安装。

这将开始安装,并在屏幕上显示进度。安装完成后,向导将带您进入完成屏幕。

C:\PracticalMongoDBbin

5.4 运行 MongoDB

让我们看看如何开始运行和使用 MongoDB。先决条件是创建数据目录,这些数据目录不是由 MongoDB 创建的,因此在启动 MongoDB 之前,需要手动创建数据目录,并且您需要确保设置了适当的权限(例如 MongoDB 具有读、写和目录创建权限)。如果在创建文件夹之前启动 MongoDB,它将抛出一条错误消息,并且无法运行。

5.4.2 启动服务可以通过在 Windows 中打开命令提示符(需要以管理员身份运行)并执行以下命令来启动:

c:\>c:practicalmongodb\bin\mongod.exe

对于 Linux,mongod 进程是在 shell 中启动的。这将在本地主机界面上启动 MongoDB 数据库。它将在端口 27017 上侦听来自 mongo shell 的连接。

5.5 验证安装可以使用mongo shell访问数据库内容并允许您对 MongoDB 中的数据执行选择性查询或聚合。如上所述,mongod 应用用于启动数据库服务或守护进程。

5.6 蒙戈布 Shell 是 MongoDB 的一个交互式 shell,它允许您与 MongoDB 进行通信并执行各种操作,例如管理数据库、查询数据、创建索引等。要使用蒙戈布 Shell,请打开终端窗口并输入以下命令:

```bash

mongo

```

MongoDB shell是MongoDB标准发行版的一部分,它为MongoDB提供了一个完整的数据库接口。通过使用JavaScript环境处理存储在MongoDB中的数据,您可以完全访问该语言和所有标准函数。一旦数据库服务启动,您就可以启动mongo shell并开始使用MongoDB。这可以通过Linux中的Shell或Windows中的命令提示符来完成(以管理员身份运行)。

以下是一个示例代码块,演示如何启动mongo shell:

```shell

C:\practicalmongodb\bin\mongo.exe

C:\>C:\practicalmongodb\bin\mongo.exe

MongoDB shell version: 3.0.4

connecting to: test

>

test

```

当连接到数据库时,将自动创建数据库。如果试图访问一个不存在的数据库,MongoDB提供了自动创建数据库的特性。

下一章提供了更多关于使用mongo shell的信息。

5.7 保护部署

您知道如何通过默认配置安装和开始使用MongoDB。接下来,您需要确保存储在数据库中的数据在各个方面都是安全的。在本节中,您将了解如何保护您的数据。您将更改默认安装的配置,以确保您的数据库更加安全。

5.7.1 使用认证和授权

身份验证验证用户的身份,而授权确定用户可以对经过身份验证的数据库执行的操作级别。这意味着用户只有在使用有权访问数据库的凭证登录时才能访问数据库。这将禁用对数据库的匿名访问。用户通过身份验证后,可以使用授权来确保用户只拥有完成手头任务所需的访问权限。身份验证和授权都存在于每个数据库级别。用户存在于单个逻辑数据库的上下文中。

system.users是MongoDB中用于管理用户和角色的集合。MongoDB使用基于角色的方法进行授权(read、readWrite、readAnyDatabase等角色)。如果需要,用户管理员可以创建自定义角色。以下是一个示例system.users集合的内容:

```json

{

"_id": "practicaldb.Shaks",

"user": "Shaks",

"db": "practicaldb",

"credentials": {.......},

"roles": [.......]

}

```

以下是重构后的内容:

```json

{

"users": [

{

"user": "admin",

"pwd": "admin",

"roles": [

{

"role": "read",

"db": "practicaldb"

},

{

"role": "readWrite",

"db": "MyDB"

}

]

}

],

"systemUsers": [

{

"user": "admin",

"pwd": "admin",

"roles": [

{

"role": "system.users",

"db": null

},

{

"role": "system.roles",

"db": null

}

]

}

]

}

```

以下是重构后的文本,已保持段落结构:

MongoDB 3.0.4 版本信息:

时间:2015-07-03T23:11:10.763-0700

日志级别:CONTROL

日志类型:初始化和监听

日志内容:数据库版本 v3.0.4

MongoDB 3.0.4 版本信息:

时间:2015-07-03T23:11:10.764-0700

日志级别:CONTROL

日志类型:初始化和监听

日志内容:OpenSSL 版本为 OpenSSL 1.0.1j-fips 19 Mar 2015,Boost 库版本为 1_49

MongoDB 3.0.4 版本信息:

时间:2015-07-03T23:11:10.764-0700

日志级别:NETWORK

日志类型:初始化和监听

日志内容:正在等待连接端口 27017

创建管理员用户操作步骤(需要以管理员身份运行命令提示符):

1. 以管理员身份运行命令提示符的另一个实例,并执行mongo应用。例如:`C:\>C:\practicalmongodb\bin\mongo.exe`。

2. 在 MongoDB shell 中,切换到管理数据库:`db = db.getSiblingDB('admin')`。

3. 为用户 "AdminUser" 创建密码 "password",并分配角色 "userAdminAnyDatabase":`db.createUser({user: 'AdminUser', pwd: 'password', roles: ['userAdminAnyDatabase']})`。

成功添加用户:{"user":"AdminUser","roles":["userAdminAnyDatabase"]}

认证信息:

C:\>C:\practicalmongodb\bin\mongod.exe -auth

C:\practicalmongodb\bin\mongod.exe --help查看帮助和启动选项

2015-07-03T23:11:10.716-0700 I CONTROL Hotfix KB2731284或更高版本的更新已安装,无需清空数据文件

2015-07-03T23:11:10.716-0700 I JOURNAL [initandlisten]日志目录=C:\data\db\journal

...................................................

2015-07-03T23:11:10.763-0700 I CONTROL [initandlisten] MongoDB启动中:pid=2776 port=27017 dbpath=C:\data\db\ 64位主机=ANOC9

2015-07-03T23:11:10.763-0700 I CONTROL [initandlisten]目标操作系统:Windows 7/Windows Server 2008 R2

2015-07-03T23:11:10.763-0700 I CONTROL [initandlisten]数据库版本v3.0.4

2015-07-03T23:11:10.764-0700 I CONTROL [initandlisten]OpenSSL版本:OpenSSL 1.0.1j-fips 19 Mar 2015

MongoDB 5.7.1.3 创建用户并启用授权

在本节中,您将创建一个用户,并为新创建的用户分配一个角色。您已经使用管理员用户进行了身份验证,如下所示:

启动 MongoDB 控制台,输入以下命令:

```shell

C:>c:\practicalmongodb\bin\mongo.exe

```

显示 MongoDB shell 版本信息:

```sql

MongoDB shell version: 3.0.4

connecting to: test

```

切换到数据库 admin:

```sql

>use admin

switched to db admin

```

进行身份验证,输入用户名和密码:

```shell

>db.auth("AdminUser", "password")

```

成功验证后,会返回数字 1。

创建新用户 Alice 并设置密码为 Moon1234,同时为其分配只读权限:

```javascript

Product

>use product

switched to db product

>db.createUser({user: "Alice", pwd:"Moon1234", roles: [ "read" ]})

```

成功添加用户:{"user":"Alice","roles":["read"]}

接下来,验证用户对数据库具有只读访问权限:

```

> db

product

> show users

{

"_id" : "product.Alice",

"user" : "Alice",

"db" : "product",

"roles" : [

{

"role" : "read",

"db" : "product"

}

]

}

Products

C:\>c:\practicalmongodb\bin\mongo.exe -u Alice -p Moon1234 product

2015-07-03T23:11:10.716-0700 I CONTROL Hotfix KB2731284 or later update is installed, no need to zero-out data files

MongoDB shell version: 3.0.4

connecting to: products

Post successful authentication the following entry will be seen on the mongod console.

2015-07-03T23:11:26.742-0700 I ACCESS [conn2] Successfully authenticated as principal Alice on product

控制对网络的访问

默认情况下,mongod和mongos绑定到系统上所有可用的IP地址。在本节中,您将了解用于限制网络暴露的配置选项。以下代码在Windows平台上执行:

C:\>c:\practicalmongodb\bin\mongod.exe --bind_ip 127.0.0.1 --port 27017 --rest

```

在MongoDB的日志信息中,我们可以看到MongoDB已经成功启动并运行在一个Windows主机上。以下是该日志信息的重构:

时间戳:2015-07-03T00:33:49.929-0700

信息类型:控制台消息

输出信息:“Hotfix KB2731284 or later update is installed, no need to zero out data files”

时间戳:2015-07-03T00:33:49.946-0700

信息类型:日志消息

输出信息:“journal dir=C:\data\db\journal”

时间戳:2015-07-03T00:33:49.980-0700

信息类型:控制台消息和日志消息

输出信息:“MongoDB starting : pid=1144 port=27017 dbpath=C:\data\db\ 64-bit host=ANOC9”

输出日志信息:“targetMinOS: Windows 7/Windows Server 2008 R2”

输出日志信息:“db version v3.0.4”

输出日志信息:“OpenSSL version: OpenSSL1.0.1j-fips 19 Mar 2015”

输出日志信息:“build info: windows sys.getwindowsversion(major=6, minor=1, build=7601, platform=2, service_pack='Service Pack 1') BOOST_LIB_VERSION=1_49”

输出日志信息:“allocator: system”

015-07-03T00:33:49.981-0700 I CONTROL [initandlisten] options: { net: { bindIp: "127.0.0.1", http: { RESTInterfaceEnabled: true, enabled: true }, port: 27017} }

2015-07-03T00:33:49.990-0700 I NETWORK [initandlisten] waiting for connections on port 27017

2015-07-03T00:33:49.990-0700 I NETWORK [websvr] admin web console waiting for connections on port 28017

2015-07-03T00:34:22.277-0700 I NETWORK [initandlisten] connection accepted from 127.0.0.1:49164 #1 (1 connection now open)

仅更改端口并不能降低太多风险。为了完全保护环境,您需要仅允许受信任的客户端使用防火墙设置连接到端口。

更改此端口也会更改 HTTP 状态接口端口,默认端口为 28017。此端口在 X+1000 端口上可用,其中 X 代表连接端口。

此网页公开了诊断和监控信息,其中包括运行数据、各种日志和有关数据库实例的状态报告。它提供可用于管理目的的管理级统计数据。默认情况下,此页面是只读的;为了使它完全交互,您将使用 REST 设置。这种配置使页面具有充分的交互性,有助于管理员解决任何性能问题。使用防火墙时,应该只允许可信客户端访问此端口。

建议在生产环境中禁用 HTTP 状态页面以及 REST 配置。

5.7.2.1 使用防火墙

防火墙用于控制网络中的访问。它们可用于允许从特定 IP 地址到特定 IP 端口的访问,或阻止来自任何不受信任主机的任何访问。它们可以用来为 mongod 实例创建一个可信的环境,在这个环境中,您可以指定哪些 IP 地址或主机可以连接到 mongod 的哪些端口或接口。

netsh

:\> netsh advfirewall firewall add rule name="Open mongod port 27017" dir=in action=allow protocol=TCP localport=27017

Ok.

C:\>

这段代码说明所有的传入流量都允许通过端口 27017,因此任何应用服务器都可以连接到 mongod。

5.7.2.2 加密数据

C:\data\db/data/db

此外,应实施操作系统级机制,如文件系统级加密和权限,以防止对文件的未授权访问。

5.7.2.3 加密通信

通常要求 mongod 和客户机(例如 mongo shell)之间的通信是加密的。在这个设置中,您将看到如何通过配置 SSL 为上述安装增加一个安全级别,以便 mongod 和 mongo shell(客户机)之间的通信使用 SSL 证书和密钥。

建议使用 SSL 进行服务器和客户端之间的通信。从版本 3.0 开始,大多数 MongoDB 发行版现在都支持 SSL。以下命令在 Windows 平台上执行。

在本书中,您将使用以下命令来生成自签名证书和私钥。

C:\> cd c:\OpenSSL-Win64\bin

C:\OpenSSL-Win64\bin\>\openssl

OpenSSL> req -new -x509 -days 365 -nodes -out C:\practicalmongodb\mongodb-cert.crt -keyout C:\practicalmongodb\mongodb-cert.key

mongodb-cert.key

C:\practicalmongodb.pem

C:\> more C:\practicalmongodb\mongodb-cert.key > temp

C:\> copy \b temp C:\practicalmongodb\mongodb-cert.crt > C:\practicalmongodb\mongodb.pem

以下是重构后的内容:

```

C:\> C:\practicalmongodb\bin\mongod –sslMode requireSSL --sslPEMKeyFile C:\practicalmongodb\mongodb.pem

2015-07-03T03:45:33.248-0700 I CONTROL Hotfix KB2731284 or later update is installed, no need to zero-out data files

2015-07-03T02:54:30.630-0700 I JOURNAL [initandlisten] journal dir=C:\data\db\journal

2015-07-03T02:54:30.670-0700 I CONTROL [initandlisten] MongoDB starting : pid=2

816 port=27017 dbpath=C:\data\db\ 64-bit host=ANOC9

2015-07-03T02:54:30.670-0700 I CONTROL [initandlisten] targetMinOS: Windows 7/Windows Server 2008 R2

2015-07-03T02:54:30.670-0700 I CONTROL [initandlisten] db version v3.0.4

2015-07-03T02:54:30.670-0700 I CONTROL [initandlisten] OpenSSL version: OpenSSL1.0.1j-fips 19 Mar 2015

2015-07-03T02:54:30.671-0700 I CONTROL [initandlisten] build info: windows sys. getwindowsversion(major=6, minor=1, build=7601, platform=2, service_pack='Service Pack 1') BOOST_LIB_VERSION=1_49

2015-07-03T02:54:30.671-0700 I CONTROL [initandlisten] allocator: system

```

MongoDB启动日志显示,它正在监听端口27017并使用SSL进行通信。这是通过在配置文件中指定`net: { ssl: { PEMKeyFile: "c:\\practicalmongodb\\mongodb.pem", mode: "requireSSL" } }`来实现的,这意味着它要求客户端提供有效的SSL证书来进行安全的连接。

然后,从IP地址127.0.0.1:49194收到了一条消息,表明已经接受了一个到此服务器的连接,此时服务器上已经有了一个活动的连接。这是通过在命令行输入“mongo --ssl”并添加`--sslAllowInvalidCertificates`选项来完成的,以允许无效或自签名的证书。

请注意,不建议在生产环境中使用自签名证书,除非该证书在一个受信任的网络中。如果在不受信任的网络中使用自签名证书,可能会容易受到中间人攻击。

此外,日志还显示已经成功安装了更新KB2731284或更高版本,无需对数据文件进行清零操作。

最后提到的是,MongoDB shell版本是3.0.4,并且正在尝试连接到名为“test”的数据库。

MongoDB Cloud Manager是数据库开发者内置的一个监控解决方案。在2.6版之前,MongoDB云管理器(以前称为MongoDB监控服务或MMS)仅用于监控和管理MongoDB。从2.6版开始,MongoDB Cloud Manager引入了一些重要的增强功能,包括备份、时间点恢复和自动化特性,使得操作MongoDB的任务比以前更简单。自动化特性为管理员提供了强大的功能,只需点击几下鼠标就可以快速创建、升级、扩展或关闭MongoDB实例。

在本书中的这一部分,您将看到如何开始使用MongoDB云管理器。您将使用MongoDB云管理器在AWS上部署一个独立的MongoDB实例。当您启动MongoDB云管理器时,它会要求在每台服务器上安装一个自动化代理,然后由MongoDB云管理器用来与服务器通信。为了开始供应,您首先需要在MongoDB Cloud Manager上创建您的配置文件。

访问https://cloud.mongodb.com,由于您是第一次开始,请单击“注册免费”按钮。这将使您进入图5-1所示的页面。

图5-1:Account Profile

在这里,您将创建一个新的个人资料。然而,MongoDB提供了作为现有云管理器组加入的选项。输入所有相关细节,如图5-1所示,点击继续。这会将您转到提供公司信息的页面。完成个人资料和公司信息后,接受条款并单击创建帐户按钮。这就完成了概要文件的创建。

下一步是创建一个组(图5-2)。

图5-2:Create Group

为该组提供一个唯一的名称,然后单击创建组。接下来是部署选择页面,如图5-3所示,您可以选择构建新的部署或管理现有的部署。

图5-3:Deployment

选择以构建新部署。接下来,将提示您构建部署的位置(即本地、AWS或其他远程环境)。在本例中,选择AWS。单击“在AWS中部署”选项将引导您在自行调配和使用Cloud Manager进行调配之间进行选择。选择“I will Provision”选项,这意味着您将使用已经在AWS上提供给您的机器。

下一个屏幕提供了部署类型的选项(即独立、副本集或分片集群)。您正在进行独立部署,因此请单击“创建独立”框。这将使您进入图5-4所示的屏幕。

非常感谢您提供的信息。根据我所了解的,MongoDB 云管理器可以在任何连接互联网的服务器上部署 MongoDB 副本集、分片集群和独立服务器。服务器只需要能够与云管理器建立出站 TCP 连接即可。如果您需要在 Windows 或 Linux 平台上安装 MongoDB,可以参考以下链接中提供的详细步骤:

MongoDB shell是MongoDB的标准发行版所附带的一个工具。它提供了一个JavaScript环境,使您能够完全访问该语言及其标准函数。此外,它还提供了一个完整的接口,用于与MongoDB数据库进行交互。

在本章中,我们将学习mongo shell的基础知识以及如何使用它来管理MongoDB文档。在深入研究创建与数据库交互的应用之前,理解MongoDB shell的工作原理是非常重要的。

没有比从MongoDB shell开始体验MongoDB数据库更好的方法了。为了帮助读者更容易地理解和实践这些概念,本节将分为三个部分:第一部分涵盖了数据库的基本特性,包括基本的CRUD操作符;下一节将介绍高级查询;最后一节解释了存储和检索数据的两种方式:嵌入和引用。

6.1 基本查询

本节将简要讨论CRUD操作(创建、读取、更新和删除)。通过使用基本的示例和练习,您将学习如何在MongoDB中执行这些操作。此外,您还将了解在MongoDB中查询是如何执行的。

与用于查询的传统SQL不同,MongoDB使用自己的类似JSON的查询语言从存储的数据中检索信息。要启动MongoDB shell,只需执行mongo可执行文件即可。在本例中,我们将通过以下命令启动MongoDB shell:

```bash

C:\practicalmongodb\bin\mongod.exe C:\practicalmongodb

C:\>c:\practicalmongodb\bin\mongod.exe

```

2015-07-06T02:29:24.501-0700 I CONTROL Hotfix KB2731284 or later update is insalled, no need to zero-out data files

2015-07-06T02:29:24.522-0700 I JOURNAL [initandlisten] journal dir=c:\data\db\ournal

```

MongoDB 服务器已启动,监听在 localhost 的 27017 端口上。您可以使用 mongo shell 向服务器发送命令。

以下是一些常用的 MongoDB shell 命令:

1. `db.version()`:显示当前 MongoDB 实例的版本信息。

2. `db.createCollection("collection_name")`:创建一个名为 "collection_name" 的新集合。

3. `db.collection_name.insertOne(document)`:向名为 "collection_name" 的集合中插入一个文档。

4. `db.collection_name.find()`:查询名为 "collection_name" 的集合中的数据。

5. `db.collection_name.updateOne(query, update)`:更新名为 "collection_name" 的集合中满足查询条件的第一个文档。

6. `db.collection_name.deleteOne(query)`:删除名为 "collection_name" 的集合中满足查询条件的第一个文档。

要开始使用这些命令,请打开终端或命令提示符,然后输入 `mongo` 并按 Enter 键。接下来,您将进入 MongoDB shell,可以在其中输入上述命令并查看结果。

在查看mongo shell之前,让我们先简单了解一下如何使用导入/导出工具将数据导入和导出MongoDB数据库。

首先,创建一个CSV文件,用以下结构保存学生的记录:Name, Gender, Class, Score, Age。CSV的样本数据如图6-1所示。

图6-1。

Sample CSV file

接下来,将数据从MongoDB数据库导入到一个新的集合中,以便了解导入工具是如何工作的。

```shell

import

C:\>c:\practicalmongodb\bin\mongoimport.exe --help

Import CSV, TSV or JSON data into MongoDB.

When importing JSON documents, each document must be a separate line of the input file.

Example:

mongoimport --host myhost --db my_cms --collection docs < mydocfile.json

....

C:\>

exporteg.csvMyDBimporteg

C:\>c:\practicalmongodb\bin\mongoimport.exe --host localhost --db mydb --collection importeg --type csv --file c:\exporteg.csv --headerline

2015-07-06T01:53:23.537-0700 connected to: localhost

2015-07-06T01:53:23.608-0700 imported 15 documents

localhost

C:\PracticalMongoDB\bin\mongo.exeC:\PracticalMongoDB\

localhost

C:\>c:\practicalmongodb\bin\mongo.exe

MongoDB shell version: 3.0.4

connecting to: test

> use mydb

switched to db mydb

> show collections

```

根据提供的内容,您正在使用MongoDB数据库。您已经创建了一个名为"importeg"的数据库和一个名为"mycoll"的集合。要查看"importeg"数据库中的所有对象,您可以使用以下命令:

```python

db.importeg.find()

```

这将显示集合中的所有文档,如下所示:

```json

{

"_id": ObjectId("5450af58c770b7161eefd31d"),

"Name": "S1",

"Gender": "M",

"Class": "C1",

"Score": 95,

"Age": 25

}

.......

{

"_id": ObjectId("5450af59c770b7161eefd31e"),

"Name": "S2",

"Gender": "M",

"Class": "C1",

"Score": 85,

"Age": 18

}

```

以下是重构后的文本:

退出mongo shell。

db.help()显示了数据库方法,其中包括添加用户、关闭服务器、统计信息和版本。

testuse切换到mydb数据库。

在开始探索之前,让我们先简要地看一下与 SQL 术语和概念相对应的 MongoDB 术语和概念。表 6-1 对此进行了总结。

MYDBPOC

> use mydbpoc

切换到db mydbpoc。

>

testMYDBPOCdb

> db

mydbpoc。

>

MYDBPOCshow dbsshow dbsMYDBPOC显示了数据库集合。

users集合包含了三个文档,每个文档都有一个唯一的_id字段和其他字段,如First Name、Last Name、Age、Gender和Country。

and集合也包含了三个文档,每个文档都有一个唯一的_id字段和其他字段,如Name、Age、Gender和Country。

and集合中的第三个文档缺少_id字段。

创建和插入现在,您将了解如何创建数据库和集合。如前所述,MongoDB 中的文档是 JSON 格式的。

在MongoDB中创建文档的过程如下:

首先,连接到数据库并创建一个名为"mydb"的数据库:

```javascript

use mydb;

```

接下来,使用`db.createCollection()`方法创建一个名为"users"的集合:

```javascript

db.createCollection("users");

```

然后,插入两个文档"user1"和"user2":

```javascript

user1 = {FName: "Test", LName: "User", Age: 30, Gender: "M", Country: "US"};

user2 = {Name: "Test User", Age: 45, Gender: "F", Country: "US"};

db.users.insert(user1);

db.users.insert(user2);

```

接着,查看所有数据库、集合和索引:

```javascript

show dbs;

show collections;

show index();

```

最后,使用`db.users.find()`方法查询"users"集合中的所有文档:

```javascript

db.users.find();

```

在MongoDB中,我们可以使用以下方法插入文档:

1. 隐式创建集合:在插入文档之前,确保集合已经存在。如果集合不存在,MongoDB会自动创建它。例如:

```javascript

db.users.insert({"Name": "Test User", "Age": 30, "Gender": "M", "Country": "US"})

```

2. 显式创建集合:在插入文档之前,使用`db.createCollection()`方法显式创建一个集合。例如:

```javascript

db.createCollection("users")

```

然后,我们可以使用for循环将文档添加到集合中:

```javascript

for (var i = 1; i <= 20; i++) {

db.users.insert({"Name": "Test User" + i, "Age": 10 + i, "Gender": "F", "Country": "India"})

}

```

请根据提供的内容完成内容重构,并保持段落结构:

```markdown

find

> db.users.find()

{ "_id" : ObjectId("52f48cf474f8fdcfcae84f79"), "FName" : "Test", "LName" : "User", "Age" : 30, "Gender" : "M", "Country" : "US" }

{ "_id" : ObjectId("52f48cfb74f8fdcfcae84f7a"), "Name" : "Test User", "Age" : 45, "Gender" : "F", "Country" : "US" }

................

{ "_id" : ObjectId("52f48eeb74f8fdcfcae84f8c"), "Name" : "Test User18", "Age" : 28, "Gender" : "F", "Country" : "India" }

Type "it" for more

> it

{ "_id" : ObjectId("52f48eeb74f8fdcfcae84f8d"), "Name" : "Test User19", "Age" : ...

```

根据提供的内容,重构后的段落结构如下:

1. 插入两个文档:

```

{ "_id" : ObjectId("52f48eeb74f8fdcfcae84f8e"), "Name" : "Test User1", "Age" : 29, "Gender" : "F", "Country" : "India" }

{ "_id" : ObjectId("52f48cfb74f8fdcfcae84f7a"), "Name" : "Test User20", "Age" : 30, "Gender" : "F", "Country" : "India" }

```

2. 因为只剩下两个文档,所以它显示剩下的两个文档。

3. 通过显式指定 _id 进行插入:

```

db.users.insert({"_id": 10, "Name": "explicit id"})

```

4. 确认插入操作:

```

db.users.find()

```

5. 将所有女性用户的国家更新为英国:

```

db.users.update({"Gender": "F"}, {$set: {"Country": "UK"}})

```

6. 查找更新后的女性用户:

```

db.users.find({"Gender": "F"})

```

请根据提供的内容完成内容重构,并保持段落结构:

原始数据:

```json

{

"_id": ObjectId("52f48eeb74f8fdcfcae84f7b"),

"Name": "Test User1",

"Age": 11,

"Gender": "F",

"Country": "India"

}

{

"_id": ObjectId("52f48eeb74f8fdcfcae84f7c"),

"Name": "Test User2",

"Age": 12,

"Gender": "F",

"Country": "India"

}

...................

```

更新操作:

```javascript

db.users.update({"Gender": "F"}, {$set: {"Country": "UK"}}, {multi: true});

```

查询结果:

```json

{

"_id": ObjectId("52f48cfb74f8fdcfcae84f7a"),

"Name": "Test User",

"Age": 45,

"Gender": "F",

"Country": "UK"

}

..............

```

如您所见,所有符合条件的记录的国家都更新为英国。在实际的应用中工作时,您可能会遇到一种模式演变,最终可能会在文档中添加或删除字段。让我们看看如何在 MongoDB 数据库中执行这些更改。

首先,使用`update()`方法将所有用户的"Company"字段设置为"TestComp":

```javascript

db.users.update({}, { $set: { "Company": "TestComp" } }, { multi: true });

```

然后,使用`find()`方法查找所有用户并打印结果:

```javascript

db.users.find();

```

接下来,使用`update()`方法将除了特定用户之外的所有用户的"Company"字段设置为空字符串:

```javascript

db.users.update({}, { $unset: { "Company": "" } }, { multi: true });

```

最后,再次使用`find()`方法查找所有用户并打印结果。注意,这次我们只更新了部分用户,所以结果中只有特定的用户显示了新的"Company"值。

"Age": 30, "Country": "US",

"FName": "Test",

"Gender": "M",

"LName": "User",

"_id": ObjectId("52f48cf474f8fdcfcae84f79")

}

删除操作:

```javascript

db.users.remove({"Gender": "M"})

db.users.find({"Gender": "M"})

```

不返回任何文档。

删除所有文档的操作:

```javascript

db.users.remove({})

db.users.find()

```

如您所见,没有文档被返回。

最后,如果您想要删除集合,以下命令将删除集合:

```javascript

db.users.drop()

show collections

```

正如您所看到的,没有显示集合名称,这表明集合已经从数据库中删除。

```javascript// 定义用户1

var user1 = {

"Name": "Test User",

"Age": 45,

"Gender": "M",

"Country": "US"

};

// 定义用户2

var user2 = {

"Name": "Test User",

"Age": 45,

"Gender": "F",

"Country": "US"

};

// 将用户1和用户2插入数据库

db.users.insert(user1);

db.users.insert(user2);

// 插入20个新用户到数据库

for (var i = 1; i <= 20; i++) {

db.users.insert({

"Name": "Test User" + i,

"Age": 10 + i,

"Gender": "F",

"Country": "India"

});

}

// 查询所有用户信息

db.users.find();

db.users.find();

// 查询单个用户信息

db.users.findOne({"_id": ObjectId("52f4a823958073ea07e15070")}); // FName: Test, LName: User, Age: 30, Gender: M, Country: US

db.users.findOne({"_id": ObjectId("52f4a826958073ea07e15071")}); // FName: Test User, Age: 45, Gender: F, Country: US

```

id: ObjectId("52f4a83f958073ea07e15083"),

Name: "Test User18",

Age: 28,

Gender: "F",

Country: "India"}

查询文档的类型为 "it"。在 MongoDB 中,可以使用 find() 函数来查询文档。find({}) 表示查询所有文档,而 find({})find() 表示查询所有女性用户。选择器类似于 SQL 中的 where 条件或用于过滤结果的过滤器,投影仪类似于用于显示数据字段的选择条件或选择列表。

例如,要返回所有女性用户,可以使用以下命令:

```javascript

db.users.find({"Gender": "F"})

```

要细化搜索,可以将不同条件合并在一起的操作符。例如,要寻找来自印度的女性用户,可以使用以下命令:

```javascript

db.users.find({"Gender": "F", "Country": "India"})

```

在 MongoDB 数据库中,您可以使用 `$or` 运算符来实现"或"逻辑。以下是根据提供的内容重构的代码:

```javascript

db.users.find({

"Gender": "F",

$or: [

{

"Country": "India"

},

{

"Country": "US"

}

]

})

```

这段代码将查找所有性别为女性且国家为印度或美国的用户。

"_id": ObjectId("52f4a83f958073ea07e15084"),

"Name": "Test User19",

"Age": 29,

"Gender": "F",

"Country": "India"

}

请根据提供的内容完成内容重构,并保持段落结构:

在 MongoDB 中,我们可以使用 `find()` 方法来查询数据。如果想要找出居住在印度或美国的女性用户的数量,可以执行以下命令:

```javascript

db.users.find({

"Gender": "F",

$or: [

{

"Country": "India"

},

{

"Country": "US"

}

]

}).count()

```

这将返回符合条件的女性用户数量。

要按姓名降序和年龄升序显示记录,请执行以下命令:

```javascript

db.users.find({"Gender": "F"}, {"Name": 1, "Age": 1}).sort({"Name": -1, "Age": 1})

```

结果如下:

```json

{

"_id": ObjectId("52f4a83f958073ea07e1507a"),

"Name": "Test User9",

"Age": 19

}

............

```

id: ObjectId("52f4a83f958073ea07e15072")

Name: "Test User1"

Age: 11

类型 "it" 以获取更多信息。

6.1.7.5 极限(limit)

限制结果集,只返回两个用户。执行以下命令:

```javascript

db.users.find({"Gender": "F", $or: [{"Country": "India"}, {"Country": "US"}]}).limit(2)

```

结果:

```json

{

"_id": ObjectId("52f4a826958073ea07e15071"),

"Name": "Test User",

"Age": 45,

"Gender": "F",

"Country": "US"

}

{

"_id": ObjectId("52f4a83f958073ea07e15072"),

"Name": "Test User1",

"Age": 11,

"Gender": "F",

"Country": "India"

}

```

```python# 导入pymongo库

import pymongo

# 连接MongoDB数据库

client = pymongo.MongoClient("mongodb://localhost:27017/")

db = client["mydatabase"]

users_collection = db["users"]

# 查询性别为女性的用户,并只返回姓名和年龄字段

result = users_collection.find({"Gender": "F"}, {"Name": 1, "Age": 1})

# 遍历查询结果并打印

for user in result:

print(user)

```

在MongoDB中,我们可以使用`find()`方法查询满足特定条件的文档。在这个例子中,我们查询国家为"US"的用户。以下是重构后的代码:

```javascript

var c = db.users.find({"Country": "US"});

while (c.hasNext()) {

printjson(c.next());

}

```

这段代码首先使用`find()`方法查询国家为"US"的用户,然后使用`while`循环遍历查询结果。在循环中,我们使用`printjson()`函数打印每个文档的JSON格式。

如果我们只想显示数组索引1处的文档,可以使用以下代码:

```javascript

var c = db.users.find({"Country": "US"});

printjson(c[1]);

```

这段代码同样首先使用`find()`方法查询国家为"US"的用户,然后使用`printjson()`函数打印数组索引1处的文档。

以下是重构后的代码:

```javascript

db.users.find({"Name": "Test User"}).explain("allPlansExecution")

```

执行结果如下:

```json

{

"queryPlanner": {

"plannerVersion": 1,

"namespace": "mydbproc.users",

"indexFilterSet": false,

"parsedQuery": {

"$and": []

},

"winningPlan": {

"stage": "COLLSCAN",

"filter": {

"$and": []

},

"direction": "forward"

},

"rejectedPlans": []

},

"executionStats": {

"executionSuccess": true,

"nReturned": 20,

"executionTimeMillis": 0,

"totalKeysExamined": 0,

"totalDocsExamined": 20,

"executionStages": {

"stage": "COLLSCAN",

"filter": {

"$and": []

},

"nReturned": 20,

"executionTimeMillisEstimate": 0,

"works": 22,

"advanced": 20,

"needTime": 1,

"needFetch": 0,

"saveState": 0,

"restoreState": 0

}

}

}

```

"isEOF" : 1,

"invalidates" : 0,

"direction" : "forward",

"docsExamined" : 20

},

"allPlansExecution" : []

},

"serverInfo" : {

"host" : "ANAC9",

"port" : 27017,

"version" : "3.0.4",

"gitVersion" : "534b5a3f9d10f00cd27737fbcd951032248b5952"

},

"ok" : 1

}

以下是重构后的代码:

```json

{

"winningPlan": {

"stage": "COLLSCAN",

"filter": {

"Name": {

"$eq": "user101"

}

},

"direction": "forward"

},

"rejectedPlans": [],

"executionStats": {

"executionSuccess": true,

"nReturned": 1,

"executionTimeMillis": 645,

"totalKeysExamined": 0,

"totalDocsExamined": 1000000,

"executionStages": {

"stage": "COLLSCAN",

"filter": {

"Name": {

"$eq": "user101"

}

},

"nReturned": 1,

"executionTimeMillisEstimate": 20,

"works": 1000002,

"advanced": 1,

"needTime": 1000000,

"needFetch": 0,

"saveState": 7812,

"restoreState": 7812,

"isEOF": 1,

"invalidates": 0,

"direction": "forward",

"docsExamined": 1000000

},

"allPlansExecution": []

},

"serverInfo": {}

}

```

根据提供的内容,我们可以重构如下:

```json

{

"host": "ANOC9",

"port": 27017,

"version": "3.0.4",

"gitVersion": "534b5a3f9d10f00cd27737fbcd951032248b5952"

},

{

"ok": 1

}

```

如您所见,数据库扫描了整个表。这会对性能产生重大影响,因为没有索引。为了解决这个问题,我们可以创建一个单键索引。以下是创建索引的代码:

```javascript

db.testindx.ensureIndex({"Name": 1})

```

创建索引需要几分钟时间,具体取决于服务器和集合大小。使用`explain()`方法可以查看查询计划:

```javascript

db.testindx.find({"Name": "user101"}).explain("allPathsExecution")

```

查询计划显示,使用了`IXSCAN`阶段进行扫描。要优化查询性能,可以在`Name`字段上创建一个名为`Name_1`的单键索引。

"name": [

["user101", "user101"]

],

"rejectedPlans": [],

"executionStats": {

"executionSuccess": true,

"nReturned": 1,

"executionTimeMillis": 0,

"totalKeysExamined": 1,

"totalDocsExamined": 1,

"executionStages": {

"stage": "FETCH",

"nReturned": 1,

"executionTimeMillisEstimate": 0,

"works": 2,

"advanced": 1,

"needTime": 0,

"needFetch": 0,

"saveState": 0,

"restoreState": 0,

"isEOF": true,

"invalidates": 0,

"docsExamined": 1,

"alreadyHasObj": false,

"inputStage": {

"stage": "IXSCAN",

"nReturned": 1,

"executionTimeMillisEstimate": 0,

"works": 2,

"advanced": true,

"needTime": 0,

"needFetch": 0,

"saveState": 0,

"restoreState": 0,

"isEOF": true

}

}

}

根据提供的信息,这是一个查询执行计划的信息。查询中没有表扫描,但是使用了索引。索引的创建对查询执行时间有很大的影响。查询涉及到两个字段:Name和Age。以下是查询的相关信息:

```json

{

"queryPlanner": {

"plannerVersion": 1,

"namespace": "test.collstats",

"indexFilterSet": false,

"parsedQuery": {},

"winningPlan": {

"planId": "IndexScan_5",

"stages": [

{

"stageType": "INDEXSCAN",

"keyPattern": {"Name": 1},

"indexName": "testindxNameAge",

"isMultiKey": false,

"direction": "forward",

"indexBounds": {"Name": [["user101", "user101"]]},

"keysExamined": 2,

"dupsTested": 0,

"dupsDropped": 0,

"seenInvalidated": 0,

"matchTested": true,

"filterSet": false,

"docIdsToLoad": ["user101"],

"nodeids": []

}

],

"docsToExamine": []

},

"rejectedPlans": [],

"ok": true

},

"allPlansExecution": [],

...(省略其他信息)

}

```

以下是重构后的代码:

```javascript

db.testindx.find({"Name": "user5", "Age": {"$gt": 25}}).explain("allPlansExecution")

```

查询计划如下:

- 查询规划器版本:1

- 命名空间:mydbproc.testindx

- 索引过滤器集:false

- 解析后的查询:使用 $and 运算符组合 Name 和 Age 条件,要求 Name 等于 "user5" 且 Age 大于 25

- 获胜的计划:保留突变阶段,输入阶段为 FETCH 阶段,过滤条件为 Age 大于 25。未提供其他详细信息。

```json

{

"executionStats": {

"executionSuccess": true,

"nReturned": 1,

"executionTimeMillis": 0,

"totalKeysExamined": 1,

"totalDocsExamined": 1,

...

},

"inputStage": {

"stage": "FETCH",

"filter": {

"Age": {

"$gt": 25

}

},

"nReturned": 1,

"executionTimeMillisEstimate": 0,

"works": 2,

"advanced": 1,

"allPlansExecution": [

{

"nReturned": 1,

"executionTimeMillisEstimate": 0,

"totalKeysExamined": 1,

"totalDocsExamined": 1,

"executionStages": ...

},

{

...

}

]

},

"serverInfo": {

"host": "ANAC9",

"port": 27017,

"version": "3.0.4",

"gitVersion": "534b5a3f9d10f00cd27737fbcd951032248b5952"

}

}

```

.1.8.3 对排序操作的支持

在 MongoDB 中,使用索引字段对文档进行排序的排序操作提供了最高的性能。正如在其他数据库中一样,MongoDB 中的索引也因此有了顺序。如果使用索引来访问文档,它返回结果的顺序与索引的顺序相同。对多个字段进行排序时,需要创建复合索引。在复合索引中,输出可以是索引前缀或完整索引的排序顺序。索引前缀是复合索引的子集,复合索引包含从索引开始的一个或多个字段。

示例:

```javascript

db.testindx.ensureIndex({"Age": 1, "Name": 1, "Class": 1})

```

这将对以下查询有用:

```javascript

db.testindx.find().sort({"Age":1})

db.testindx.find().sort({"Age":1,"Name":1})

db.testindx.find().sort({"Age":1,"Name":1, "Class":1})

```

上述索引在以下查询中不会有太大帮助:

```javascript

db.testindx.find().sort({"Gender":1, "Age":1, "Name": 1})

```

6.1.8.4 唯一索引

首先,让我们删除现有的索引。

```javascript

db.testindx.dropIndexes()

```

然后创建一个唯一索引:

```javascript

db.testindx.ensureIndex({"Name":1},{"unique":true})

```

现在,如果您尝试在集合中插入重复的名称,如下所示,MongoDB 将返回一个错误,并且不允许插入重复的记录:

```javascript

db.testindx.insert({"Name": "uniquename"})

```

在MongoDB中,您可以使用唯一索引来确保集合中的文档具有唯一的组合键。但是,当您尝试插入具有相同组合键的重复文档时,将会抛出错误。以下是如何解决这个问题的方法:

1. 创建一个包含复合索引的集合。这将确保组合键是唯一的。例如:

```javascript

db.testindx.ensureIndex({ "Name": 1, "Age": 1 }, { "unique": true });

```

2. 在插入具有相同组合键的文档之前,先清空集合中的所有文档。例如:

```javascript

db.testindx.remove({});

```

3. 现在,您可以插入具有唯一组合键的文档,而不会遇到任何错误。例如:

```javascript

db.testindx.insert({ "Name": "usercit", "Age": 30 });

db.testindx.insert({ "Name": "usercit", "Age": 30 }); // 将不会抛出错误,因为"Name"和"Age"的组合已经存在于集合中

```

请注意,如果您在创建唯一索引时指定了`dropDups=true`,则在尝试插入具有相同组合键的重复文档时,索引创建将失败。因此,在这种情况下,您需要先删除集合中的所有文档,然后再尝试插入具有唯一组合键的文档。

.1.8 索引操作

本节将介绍 MongoDB 中可用的不同类型的索引。

6.1.8.1 创建索引

要创建一个索引,可以使用 `ensureIndex` 方法。例如,要为名为 `testindx` 的集合创建一个名为 `Name` 的唯一索引,可以使用以下命令:

```javascript

db.testindx.ensureIndex({"Name": 1}, {"unique": true, "dropDups": true})

```

6.1.8.2 获取索引信息

可以使用 `getIndexes` 方法获取集合的所有索引信息。例如,要获取名为 `testindx` 的集合的所有索引,可以使用以下命令:

```javascript

db.testindx.getIndexes()

```

6.1.8.3 删除索引

要删除一个索引,可以使用 `dropIndex` 方法。例如,要删除名为 `testindx` 的集合中名为 `Name` 的索引,可以使用以下命令:

```javascript

db.testindx.dropIndex({"Name": 1})

```

6.1.8.4 重新索引

要重新索引集合,可以使用 `reIndex` 方法。例如,要对名为 `testindx` 的集合进行重新索引,可以使用以下命令:

```javascript

db.testindx.reIndex()

```

6.1.8.5 系统索引

MongoDB 将索引存储在 BTree 结构中,因此自动支持范围查询。如果在一个查询中使用了多个选择标准,MongoDB 会尝试找到最佳的单个索引来选择一个候选集。之后,它依次遍历集合,以评估其他标准。当第一次执行查询时,MongoDB 为查询可用的每个索引创建多个执行计划。它让计划在一定数量的周期内轮流执行,直到执行最快的计划完成。然后将结果返回给系统,系统会记住最快的执行计划所使用的索引。对于后续查询,将使用记住的索引,直到集合中发生了一定数量的更新。超过更新限制后,系统将再次执行该过程,以找出当时适用的最佳索引。当下列任一事件发生时,将重新评估查询计划:集合接收 1,000 次写操作;添加或删除了一个索引。

mongod 进程重启后,发生了用于重建索引的重新索引。在 MongoDB 2.6 版本之前,MongoDB 在任何时候都只使用一个索引,因此您需要确保存在复合索引来更好地匹配您的查询。这可以通过检查查询的排序和搜索标准来完成。

索引交集是在 MongoDB 2.6 版本中引入的。它支持索引的交集,以满足具有复合条件的查询,其中条件的一部分由一个索引满足,而另一部分由另一个索引满足。一般来说,一个索引交集由两个索引组成;但是,可以使用多个索引交集来解析查询。这种能力提供了更好的优化。与其他数据库一样,索引维护也有成本。每个更改集合的操作(比如创建、更新或删除)都有开销,因为索引也需要更新。为了保持最佳平衡,您需要定期检查索引的有效性,该索引可以通过您在系统上执行的读写操作的比率来衡量。找出不常用的索引并删除它们。

6.2 超越基础

本节将介绍在选择器部分使用条件操作符和正则表达式的高级查询。这些操作符和正则表达式中的每一个都为您提供了对所编写的查询的更多控制,从而也为您提供了对从 MongoDB 数据库中获取的信息的更多控制。

使用条件运算符:$lt$lte$gt$gte$in$nin$not

示例文档:

```json

{

_id: ObjectID(),

Name: "Full Name",

Age: 30,

Gender: "M",

Class: "C1",

Score: 95

}

```

首先创建集合并插入几个示例文档:

```javascript

db.students.insert({Name:"S1",Age:25,Gender:"M",Class:"C1",Score:95})

db.students.insert({Name:"S2",Age:18,Gender:"M",Class:"C1",Score:85})

db.students.insert({Name:"S3",Age:18,Gender:"F",Class:"C1",Score:85})

```

在MongoDB中,使用`insert()`方法可以向集合中插入数据。在这个例子中,我们向名为`students`的集合中插入了10条学生数据。以下是重构后的代码:

```javascript

db.students.insert({Name: "S4", Age: 18, Gender: "F", Class: "C1", Score: 75});

db.students.insert({Name: "S5", Age: 18, Gender: "F", Class: "C2", Score: 75});

db.students.insert({Name: "S6", Age: 21, Gender: "M", Class: "C2", Score: 100});

db.students.insert({Name: "S7", Age: 21, Gender: "M", Class: "C2", Score: 100});

db.students.insert({Name: "S8", Age: 25, Gender: "F", Class: "C2", Score: 100});

db.students.insert({Name: "S9", Age: 25, Gender: "F", Class: "C2", Score: 90});

db.students.insert({Name: "S10", Age: 28, Gender: "F", Class: "C3", Score: 90});

db.students.find();

```

执行上述代码后,您将看到如下所示的结果集:

```arduino

{

"_id" : ObjectId("52f874faa13cd6a65998734d"),

"Name" : "S1",

"Age" : 25,

"Gender" : "M",

"Class" : "C1",

"Score" : 95

}

.......................

```

请根据提供的内容完成内容重构,并保持段落结构:

原始内容:

```

{ "_id": ObjectId("52f8758da13cd6a659987356"), "Name": "S10", "Age": 28, "Gender": "F", "Class": "C3", "Score": 90 }

>

6.2.1.1   l   t   和   lt   和   lt和lt和lt

$lt$lte

find

> db.students.find({"Age":{"$lt":"25"}})

{ "_id": ObjectId("52f8750ca13cd6a65998734e"), "Name": "S2", "Age": 18, "Gender": "M", "Class": "C1", "Score": 85 }

.............................

{ "_id": ObjectId("52f87556a13cd6a659987353"), "Name": "S7", "Age": 21, "Gender": "M", "Class": "C2", "Score": 100 }

>

如果您想找出所有大于 25 岁(年龄<= 25)的学生,请执行以下命令:

> db.students.find({"Age":{"$lte":"25"}})

```

重构后的内容:

查询年龄小于等于25岁的学生:

```javascript

db.students.find({"Age": {"$lte": 25}})

```

请根据提供的内容完成内容重构,并保持段落结构:

原始内容:

```

{ "_id" : ObjectId("52f874faa13cd6a65998734d"), "Name" : "S1", "Age" : 25, "Gender" : "M", "Class" : "C1", "Score" : 95 }

.....................

{ "_id" : ObjectId("52f87578a13cd6a659987355"), "Name" : "S9", "Age" : 25, "Gender" : "F", "Class" : "C2", "Score" : 90 }

>

6.2.1.2 $gt 和$gte

$gt$gte

让我们找出所有年龄大于 25 岁的学生。这可以通过执行以下命令来实现:

> db.students.find({"Age":{">":25}})

{ "_id" : ObjectId("52f8758da13cd6a659987356"), "Name" : "S10", "Age" : 28, "Gender" : "F", "Class" : "C3", "Score" : 90 }

>

如果将上述示例更改为返回年龄 >= 25 岁的学生,则命令为

> db.students.find({"Age":{"$gte":25}})

```

根据提供的内容,这是一段MongoDB查询语句,用于从名为"students"的集合中查找属于C1班或C2班的所有学生。查询结果如下:

```json

{

"_id": ObjectId("52f874faa13cd6a65998734d"),

"Name": "S1",

"Age": 25,

"Gender": "M",

"Class": "C1",

"Score": 95

}

```

以及:

```json

{

"_id": ObjectId("52f8758da13cd6a659987356"),

"Name": "S10",

"Age": 28,

"Gender": "F",

"Class": "C3",

"Score": 90

}

```

您好!您可以使用MongoDB的聚合框架来查询多个集合。以下是一个示例,说明如何搜索名为users和products的两个集合,并返回混合的结果集:

```javascript

db.users.aggregate([

{ $lookup: { from: "products", localField: "product_id", foreignField: "_id", as: "user_products" } },

{ $unwind: "$user_products" },

{ $replaceRoot: { newRoot: "$user_products" } }

])

```

在本节中,我们将学习如何使用正则表达式。当我们需要查找以特定字符开头的姓名时,正则表达式就非常有用。例如,假设我们有一个学生数据库,其中包含不同名字和班级的学生信息。为了理解这一点,我们先添加一些具有不同名字和班级的学生数据。

```javascript

db.students.insert({Name:"Student1", Age:30, Gender:"M", Class: "Biology", Score:90})

db.students.insert({Name:"Student2", Age:30, Gender:"M", Class: "Chemistry", Score:90})

db.students.insert({Name:"Test1", Age:30, Gender:"M", Class: "Chemistry", Score:90})

db.students.insert({Name:"Test2", Age:30, Gender:"M", Class: "Chemistry", Score:90})

db.students.insert({Name:"Test3", Age:30, Gender:"M", Class: "Chemistry", Score:90})

```

现在假设我们想要查找姓名以“St”或“Te”开头且班级以“Che”开头的所有学生。同样可以使用正则表达式进行过滤,如下所示:

```javascript

db.students.find({"Name":/(St|Te)*/i, "Class":/(Che)/i})

```

请根据提供的内容完成内容重构,并保持段落结构:

```json

{

"_id": ObjectId("52f89ecae451bb7a56e59086"),

"Name": "Student2",

"Age": 30,

"Gender": "M",

"Class": "Chemistry",

"Score": 90

}

..........................

{

"_id": ObjectId("52f89f06e451bb7a56e59089"),

"Name": "Test3",

"Age": 30,

"Gender": "M",

"Class": "Chemistry",

"Score": 90

}

>

"Name":/(St|Te)*/i

//i表示正则表达式不区分大小写。

(St|Te)*表示名称字符串必须以“St”或“Te”开头。

末尾的*表示它将匹配其后的任何内容。

当您将所有内容放在一起时,您正在对以“St”或“Te”开头的名称进行不区分大小写的匹配。在该类的正则表达式中,也发出相同的正则表达式。

接下来,让我们使查询变得复杂一点。让我们把它和上面提到的操作符结合起来。

获取姓名为student1、student2且年龄>=25岁的男性学生。执行此操作的命令如下:

>db.students.find({"Name":/(student*)/i,"Age":{"$gte":25},"Gender":"M"})

{

"_id": ObjectId("52f89eb1e451bb7a56e59085"),

"Name": "Student1",

"Age": 30,

"Gender": "M",

"Class": "Chemistry",

"Score": 90

}

```

MapReduce是一种框架,用于处理高度分布在巨大数据集上并使用多个节点运行的问题。它由两部分组成:Map和Reduce。

在Map阶段,主节点接受输入参数,并将大问题分成多个小的子问题。这些子问题然后分布在工作节点上。工作者节点可以进一步将问题分成子问题。这导致了多级树结构。然后,工作节点将处理其中的子问题,并将答案返回给主节点。

在Reduce阶段,所有子问题的答案都可以通过主节点获得,然后主节点将所有答案组合起来并产生最终输出,这就是您试图解决的大问题的答案。

为了理解它是如何工作的,让我们考虑一个小例子,在这个例子中,您将找出您的集合中男女学生的数量。

Map阶段的代码如下:

```javascript

var map = function(){emit(this.Gender,1);};

```

Reduce阶段的代码如下:

```javascript

var reduce = function(key, value){return Array.sum(value);};

```

以下是重构后的代码:

```javascript

// 定义map函数

var map_1 = function() {

emit(this.Class, this.Score);

};

// 定义reduce函数

var reduce_1 = function(key, values) {

return Array.avg(values);

};

// 执行mapReduce操作,将结果输出到"MR_ClassAvg_1"集合中

db.students.mapReduce(map_1, reduce_1, { out: "MR_ClassAvg_1" });

// 查询"MR_ClassAvg_1"集合中的数据

db.MR_ClassAvg_1.find();

```

id: "C1", value: 85

_id: "C2", value: 93

_id: "C3", value: 90

_id: "Chemistry", value: 90

map{"Class": Score}, {"C1": 95} mapreduceMR_ClassAvg_1

find

6.2.4 聚合()

MapReduce

MapReduceMapReduceMapReduce

aggregate

db.students.aggregate({$group:{_id:"$Gender", totalStudent: {$sum: 1}}})

{ "_id" : "F", "totalStudent" : 6 }

{ "_id" : "M", "totalStudent" : 9 }

类似地,为了找出类的平均分数,可以执行下面的命令:

db.students.aggregate({$group:{_id:"$Class", AvgScore: {$avg: "$Score"}}})

{ "_id" : "Biology", "AvgScore" : 90 }

{ "_id" : "C3", "AvgScore" : 90 }

{ "_id" : "Chemistry", "AvgScore" : 90 }

{ "_id" : "C2", "AvgScore" : 93 }

{ "_id" : "C1", "AvgScore" : 85 }

.3 设计应用的数据模型

在本节中,您将了解如何为应用设计数据模型。MongoDB 数据库为设计数据模型提供了两种选择:用户可以将相关对象嵌入到另一个对象中,也可以使用 ID 相互引用。在本节中,您将探索这些选项。

为了理解这些选项,您将设计一个博客应用并演示这两个选项的用法。

典型的博客应用由以下场景组成:

有人在不同的主题上发表博客。除了主题分类,还可以使用不同的标签。举例来说,如果类别是政治,帖子谈论一个政治家,那么这个政治家的名字可以作为标签添加到帖子中。这有助于用户快速找到与他们兴趣相关的帖子,并让他们将相关帖子链接在一起。

浏览博客的人可以对博客文章发表评论。

6.3.1 关系数据建模和规范化

在开始使用 MongoDB 的方法之前,让我们先绕一小段路,看看如何在关系数据库(如 SQL)中对此建模。

在关系数据库中,数据建模通常是通过定义表并逐渐删除数据冗余来实现范式的。

6.3.1.1 什么是范式?

在关系数据库中,范式通常从根据应用需求创建表开始,然后逐渐移除冗余以实现最高范式,这也被称为第三范式或 3NF。为了更好地理解这一点,让我们将博客应用数据放在表格中。初始数据如图 6-2 所示。

图 6-2。

Blogging application initial data

这个数据其实是第一范式的。你会有很多冗余,因为你可以对文章有多个评论,多个标签可以与文章相关联。当然,冗余的问题是它引入了不一致的可能性,即相同数据的不同副本可能具有不同的值。要消除这种冗余,您需要通过将数据拆分到多个表中来进一步规范化数据。作为此步骤的一部分,您必须标识唯一标识表中每一行的键列,以便您可以在表之间创建链接。当使用 3NF 范式建模时,上述场景将看起来像图 6-3 所示的 RDBMs 图。

图 6-3。

RDBMS diagram

在这种情况下,您有一个没有冗余的数据模型,允许您更新它,而不必担心更新多行。特别是,您不再需要担心数据模型中的不一致性。

6.3.1.2 范式的问题

然而,当您试图取回数据时,问题出现了。例如,为了找到与特定用户的帖子相关联的所有标签和评论,关系数据库程序员使用一个 JOIN。通过使用连接,数据库根据应用屏幕设计返回所有数据,但是真正的问题是数据库执行什么操作来获得结果集。

以下是重构后的内容:

### 6.3.2 MongoDB 文档数据模型方法

在 MongoDB 中,数据以文档的形式存储。这为模式设计带来了新的可能性,但同时也使模式设计过程变得复杂。与关系数据库中的规范化不同,在 MongoDB 中,模式设计取决于您试图解决的问题。

假设我们需要使用 MongoDB 文档模型对上述博客信息进行建模,可以将博客数据存储在一个文档中如下所示:

```json

{

"_id": ObjectId("509d27069cc1ae293b36928d"),

"title": "Sample title",

"body": "Sample text.",

"tags": [

"Tag1",

"Tag2",

"Tag3",

"Tag4"

],

"created_date": ISODate("2015-07-06T12:41:39.110Z"),

"author": "Author 1",

"category_id": ObjectId("509d29709cc1ae293b369295"),

"comments": [

{

"subject": "Sample comment",

"body": "Comment Body",

"author": "author 2"

}

]

}

```

请注意,当面临模式设计问题时,不再像关系数据库那样有固定的规范化数据库设计路径。在 MongoDB 中,模式设计取决于您试图解决的问题。

```

// Authors document:

{

"_id": ObjectId("509d280e9cc1ae293b36928e"),

"name": "Author 1"

}

// Tags document:

{

"_id": ObjectId("509d35349cc1ae293b369299"),

"TagName": "Tag1"

}

// Comments document:

{

"_id": ObjectId("509d359a9cc1ae293b3692a0"),

"Author": ObjectId("508d27069cc1ae293b36928d"),

"created_date": ISODate("2015-07-06T13:34:59.336Z")

}

// Category Document

{

"_id": ObjectId("509d29709cc1ae293b369295"),

"Category": "Category1"

}

// Posts Document

{

"_id": ObjectId("509d27069cc1ae293b36928d"),

"title": "Sample title",

"body": "Sample text.",

"tags": [ObjectId("509d35349cc1ae293b369299"), ObjectId("509d35349cc1ae293b36929c")],

}

```

在本章中,我们将探讨两种解决方案:引用(References)和嵌入(Embedding)。这两种方法在不同的场景下可能会产生不同的性能影响。

6.3.2.1 嵌入

当您需要从数据集中获取一些数据并将其显示在屏幕上时,例如显示与博客相关的评论的页面,嵌入会很有用。在这种情况下,评论可以嵌入到博客文档中。这种方法的优点是,由于 MongoDB 将文档连续存储在磁盘上,所以所有相关数据都可以在一次寻道中获取。这有助于提高查询性能。此外,由于不支持连接,并且在这种情况下使用了引用,应用可能会执行多次查找才能找到相关数据。然而,如果应用经常访问博客中的评论数据,那么在博客文档中嵌入评论会对性能产生积极的影响。

支持嵌入的另一个考虑因素是在编写数据时对原子性和隔离性的需求。MongoDB 的设计没有多文档事务。在 MongoDB 中,操作的原子性只在单个文档级别提供,因此需要一起原子更新的数据需要一起放在单个文档中。当您更新数据库中的数据时,您必须确保您的更新要么成功,要么完全失败,决不能有“部分成功”,并且没有其他数据库读取器会看到未完成的写操作。

6.3.2.2 参考

尽管嵌入在许多情况下提供了最佳性能和数据一致性保证,但在某些情况下,更规范化的模型在 MongoDB 中可能表现更好。拥有多个集合并添加引用的一个原因是它增加了查询数据的灵活性。以上述博客示例为例,通过使用引用而不是嵌入,应用需要进行多次查找才能找到相关数据。这可能导致查询性能降低。因此,在选择使用引用还是嵌入时,需要根据实际需求和场景来权衡。

您已经了解了如何使用嵌入式模式,当在一个页面上显示所有数据时(即显示博客文章和所有相关评论的页面),这种模式会工作得很好。现在假设您需要搜索某个用户发布的评论。查询(使用这个嵌入式模式)如下所示:

db.posts.find({'comments.author': 'author2'}, {'comments': 1})

那么,该查询的结果将是以下形式的文档:

```json

{

"_id": ObjectId("509d27069cc1ae293b36928d"),

"comments": [

{

"subject": "Sample Comment 1",

"body": "Comment1 Body.",

"author_id": "author2",

"created_date": ISODate("2015-07-06T13:34:23.929Z")

}...]

}

```

这种方法的主要缺点是得到的数据比实际需要的多得多。特别是不能只要求 author2 的评论;你必须询问作者 2 评论过的帖子,这也包括对这些帖子的所有其他评论。这些数据需要在应用代码中进一步过滤。

另一方面,假设您决定使用规范化模式。在这种情况下,您将有三个文档:“作者”、“帖子”和“评论”。这样,您可以分别获取 author2 的帖子、评论以及帖子的其他评论,而无需在应用代码中进行额外的过滤。要实现这一点,您可以使用以下查询:

```javascript

db.authors.find({"username": "author2"}, {"posts": 1, "comments": 1})

```

这将返回一个包含 author2 的帖子和评论的文档,而无需在应用程序中进行任何额外的处理。

在这种情况下,嵌入式方法可能无法高效地处理大量评论。因此,使用引用文档的方法可能更适合这种情况。

以下是重构后的代码:

```javascript

// 作者文档:包含特定于作者的信息,如姓名、年龄、性别等。

{

"_id": ObjectId("508d280e9cc1ae293b36928e"),

"name": "Jenny",

...

}

// 帖子文档:包含特定于帖子的详细信息,如创建时间、作者和实际内容。

{

"_id": ObjectId("508d27069cc1ae293b36928d"),

...

}

// 评论文档:包含帖子的评论,如创建时间、评论者和评论文本。

{

"_id": ObjectId("508d359a9cc1ae293b3692a0"),

"Author": ObjectId("508d27069cc1ae293b36928d"),

"created_date": ISODate("2015-07-06T13:34:59.336Z"),

"Post_id": ObjectId("508d27069cc1ae293b36928d"),

...

}

```

查询示例:

```javascript

db.comments.find({"author": "author2"})

db.comments.find().sort({"created_date": -1}).limit(10)

db.comments.find().skip(20).limit(10)

```

随着文档大小的增加,MongoDB 数据库将占用更多的内存。这可能导致在检索文档时出现更多的页面错误,从而降低性能。此外,MongoDB 文档有一个 16MB 的硬性大小限制。尽管需要注意这一点,但在达到 16MB 的大小限制之前,您通常会由于内存压力和文档复制而遇到问题。

支持使用文档引用的最后一个因素是多对多或 M:N 关系。例如,在上面的例子中,有一些标签。每个博客可以有多个标签,每个标签可以与多个博客条目相关联。实现 blogs-tags M:N 关系的一种方法是拥有以下三个集合:Tags、BlogsTag-To-Blog Mapping。这种方法类似于关系数据库中的方法,但是这会对应用的性能产生负面影响,因为查询最终会执行大量应用级的“连接”。或者,您可以使用嵌入模型,在博客文档中嵌入标签,但是这将导致数据重复。虽然这将稍微简化读取操作,但它将增加更新操作的复杂性,因为在更新标签细节时,用户需要确保更新的标签在嵌入到其他博客文档中的每个地方都得到更新。

MongoDB 中的模式设计是您需要做出的最早决策之一,它取决于应用的需求和查询。正如您所看到的,当您需要一起访问数据或者需要进行原子更新时,嵌入将会产生积极的影响。但是,如果您在查询时需要更多的灵活性,或者如果您有多对多的关系,使用引用是一个不错的选择。最终,决定取决于应用的访问模式,在 MongoDB 中没有严格的规则。

例如,假设你有一个书评网站,里面有作者和书籍,还有带线索评论的评论。现在的问题是如何组织收藏。这个决定取决于每本书预期的注释数量以及执行读写操作的频率。

除了元素之间的交互方式(即是否以嵌入式方式存储文档或使用引用),在为应用设计数据模型时,许多其他操作因素也很重要。这些因素将在以下章节中介绍:数据生命周期管理、索引、分片等。

在MongoDB中,数据的组织和存储方式被称为分片。集合被划分为多个片段(称为碎片),这些碎片分布在不同的机器集群上。虽然这种设计可以提高数据处理的效率,但也可能对性能产生一定的影响,我们将在后续章节中详细讨论。

当处理大量数据时,需要考虑如何有效地组织和管理这些数据。以下是一些关于拥有多个集合与将数据存储在单个集合中的设计考虑事项:

1. 选择多个集合来存储数据不会影响性能。

2. 对于不同类型的数据拥有不同的集合可以提高高吞吐量批处理应用的性能。

3. 当您设计具有大量集合的模型时,需要考虑以下行为:_id、.ns以及文档的增长。例如,更新操作(如将一个元素推送到一个数组或添加新的字段)可能导致文档尺寸的增加,从而引发文档从一个槽移动到另一个槽以适应文档的过程。这个文档重定位过程既耗费资源又耗费时间。尽管MongoDB提供了填充功能来最小化重定位的发生,但您可能仍需要手动处理文档的增长。

在本章中,您已经学习了基本的CRUD操作和高级查询功能,并研究了存储和检索数据的两种方式:嵌入和引用。在下一章中,我们将深入了解MongoDB的架构、其核心组件和特性。