书名:MongoDB入门经典
ISBN:978-7-115-39111-7
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
• 著 [美] Brad Dayley
译 米爱中
责任编辑 傅道坤
• 人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
• 读者服务热线:(010)81055410
反盗版热线:(010)81055315
Brad Dayley: Sams Teach Yourself NoSQL with MongoDB in 24 Hours
ISBN: 0672337134
Copyright © 2014 by Pearson Education, Inc.
Authorized translation from the English languages edition published by Pearson Education, Inc.
All rights reserved.
本书中文简体字版由美国Pearson公司授权人民邮电出版社出版。未经出版者书面许可,对本书任何部分不得以任何方式复制或抄袭。
版权所有,侵权必究。
MongoDB是目前非常流行的一种非关系型数据库(NoSQL),因其操作简单、完全免费、源码公开等特点,受到了IT从业人员的青睐,并被广泛部署于实际的生产环境中。
本书采用直观、循序渐进的方法,讲解了如何设计、实施和优化NoSQL数据库,如何存储和管理数据,以及如何执行数据分片和复制等任务。本书共分为24章,其内容涵盖了NoSQL和传统RDBMS的使用时机,理解基本的MongoDB数据结构和设计概念,安装和配置MongoDB,为自己的应用选择正确的NoSQL交付模型,规划和实施不同类型和规模的MongoDB数据库,设计MongoDB数据模型,创建新的数据库、集合和索引,掌握存储、查找和获取MongoDB数据的方法,通过PHP、Python、Java和Node.js/Mongoose与数据交互,在一致性、性能和持久性方面做出平衡,对MongoDB数据库进行管理、监控、验证、保护、备份和修复;掌握数据分片和复制等高级技术;实施GridFS存储来有效地存储和获取大型数据文件,评估用于优化性能的查询,查找和诊断与集合、数据库相关的问题。
本书适合对NoSQL以及MongoDB感兴趣的数据库开发、运维人员阅读。
托夫勒先生在其著作《第三次浪潮》中将“大数据”比喻为“第三次浪潮的华彩乐章”。30余年后,“大数据”开始成为互联网行业的热词,并最终走进人们的视线——大数据的面纱被逐渐揭开,对“大数据”的各种误读也逐渐被纠正。
尽管到现在为止,对“大数据”定义的表达仍然众说纷纭,但就“大数据”的特点来讲,著名数据科学家维克托·迈尔·舍恩伯格在与肯尼斯·库克耶合著的《大数据时代》中提出的“4V”基本得到了绝大多数的认同。所谓“4V”,是指Volume(数据量大)、Velocity(输入和处理速度快)、Variety(数据多样性)、Value(价值密度低)。从技术角度讲,大数据与云计算,有着密不可分的关系,这种关系类似汽车与高速公路。显而易见,大数据不可能用单台计算机进行处理,必然会采取分布式架构,对海量数据进行分布式数据挖掘,则必须依托云计算的分布式处理、分布式数据库、云存储以及虚拟化技术。NoSQL在这样的环境下得到了迅速的普及。作为NoSQL数据库四大分类之一的文档型数据库的杰出代表——MongoDB,则因为其易部署、易使用、高性能、数据存储方便等特点,得到了广泛的应用。
本书作者Brad Dayley,算得上是骨灰级软件工程师,拥有20多年的企业级应用程序开发经验,先后就职于Adobe、Novell、John Wiley等世界知名企业。Brad Dayley也是一位非常善于表达的技术流图书作者,曾经出版过Node.js, MongoDB and AngularJS Web Development、jQuery and JavaScript in 24 Hours, Sams Teach Yourself、Python Phrasebook、Photoshop CC Bible、jQuery and JavaScript Phrasebook、Silverlight Bible等多部图书。此次国内引进出版的这本《MongoDB入门经典》,是其2014年出版的新书。
这本书在内容的安排上,遵循了非常直观易读又循序渐进的思路,用24个课程教读者学会如何打造一个实时高效的大数据解决方案,即使读者没有任何的MongoDB部署经验。如何实现MongoDB,是这本书重点着墨的地方,也是初学MongoDB的人最需要彻底弄明白的地方——创建数据库和集合,在MongoDB数据库中存储、查找与检索各种数据。
值得一提的是,这本书的代码示例很有特点,既有用来演示知识点的在正文中的代码,也有在Try It Yourself章节中出现的更加完整的代码,还可以作为小型应用程序来运行。代码示例简洁优美,而且易懂易维护,是这本书的一个特点。
这本书的原书由SAMS公司出版,中文简体版由人民邮电出版社引进版权。相信此书能极大地满足广大MongoDB初学者的需求,还可以作为国内高等院校的相关专业教材。
本书的内容非常专业,语言精妙,而译者的水平和时间都相对有限,谬误与不当之处在所难免,敬请广大读者批评指正。
河南理工大学计算机科学与技术学院 米爱中
2014年冬
我要借此机会感谢所有让本书得以付梓的人员。首先,感谢我的妻子和儿子给予灵感和支持,如果没有你们,本书根本不可能完成。感谢Mark Taber确保本书没有偏离方向,感谢Russell Kloepfer所做的技术审阅,感谢Melissa Schirmer负责印制方面的杂务。
Brad Dayley是一名资深软件工程师,拥有20多年企业级应用程序开发经验;设计并开发过大型商业应用程序,包括后端为NoSQL数据库、前端为Web的SAS应用程序;另著有jQuery and JavaScript Phrasebook、Sams Teach Yourself jQuery and JavaScript in 24 Hours和Node.js, MongoDB and AngularJS Web Development。
当前,互联网用户多达几十亿,传统的RDBMS数据库解决方案难以满足快速增长的海量数据处理需求,人们越来越多地采用专用数据库,它们不受制于传统SQL数据库的限制和开销。这些数据库被统称为NoSQL,意思是“不仅仅是SQL”;它们并非要取代SQL数据库,而是旨在提供另一种数据存储方式。
本书从MongoDB的角度讲授NoSQL概念。MongoDB是一种NoSQL数据库,以易于实现、健壮、可扩展著称,是当前使用最广泛的NoSQL数据库。MongoDB已成熟为稳定的平台,已被多家公司用来提供所需的数据可扩展性。
本书每章都介绍了将MongoDB用作高性能应用程序的后端存储所需的基本知识,阅读完本书后,您将对如何创建、使用和维护MongoDB数据库有深入认识。
请坐下来尽情享受学习MongoDB开发的旅程吧。
本书分为4个部分。
第1部分,“NoSQL和MongoDB初步”,介绍NoSQL基本概念、为何要使用NoSQL以及NoSQL数据库类型;探讨MongoDB数据结构和设计概念以及如何安装和配置MongoDB。
第2部分,“实现MongoDB”,讨论实现MongoDB的基本知识,重点是创建数据库和集合以及在MongoDB数据库中存储、查找和检索数据的各种方法。
第3部分,“在应用程序中使用MongoDB”,介绍一些最常见的编程环境中使用的MongoDB驱动程序。MongoDB驱动程序是一个库,提供了以编程方式访问和使用MongoDB数据库所需的工具。这部分涵盖了用于Java、PHP、Python和Node.js的MongoDB驱动程序;针对每种语言的内容都自成一体,让您能够跳过与您不感兴趣的语言相关的章节。
第4部分,“其他MongoDB概念”,介绍其他MongoDB概念,完善您的MongoDB知识。在这部分,您将学习MongoDB数据库管理方面的基本知识,了解复制、分片和GridFS存储等MongoDB高级概念。
本书的代码示例分两类,其中最常见的是夹杂在正文中的代码片段,旨在演示当前讨论的要点。另一种以Try It Yourself形式出现,这些代码示例更完备,可作为独立的小型应用程序运行。本书对代码示例进行了简化以确保它们短小易懂,例如,几乎没有包含错误检查代码。
为方便读者理解Try It Yourself示例代码,使用了包含行号的程序清单列出它们;在程序清单标题中,指出了代码来自哪个文件;另外,还通过独立的程序清单列出了示例代码的控制台输出,方便读者阅读本书时查看。
每章末尾都有简短的问答环节,对每位读者都会有的疑问做出了解答;简短而全面的小测验让您能够进行自测,确保您牢固地掌握了每个知识点;最后提供了一两个练习,让您有机会将新学到的知识付诸应用。
本章介绍如下内容:
在大多数大型应用程序和服务中,核心都是高性能的数据存储解决方案。后端数据存储负责存储用户账户信息、产品数据、记账信息和博客等重要数据。优秀的应用程序要求能够准确、快速、可靠地存储和检索数据,因此选择的数据存储机制的性能必须能够满足应用程序的需求。
有多种数据存储解决方案可用于存储和检索应用程序所需的数据,其中最常见的有三种:文件系统直接存储、关系型数据库和NoSQL数据库。本书介绍MongoDB,它是使用最广泛、功能最强大的NoSQL数据存储方式。
接下来的几节将介绍NoSQL和MongoDB,并讨论决定如何组织数据和配置数据库前需要考虑的设计因素。为此,将首先提出问题,再介绍能够满足这些需求的MongoDB内置机制。
一种常见的误解是,以为术语NoSQL指的是“非SQL”,它实际上指的是“不仅仅是SQL”,这旨在强调这样一点:NoSQL数据库并非SQL的替代品,它实际上也可使用类似于SQL的查询概念。
NoSQL是个包罗万象的术语,涵盖了除传统关系型数据库管理系统(RDBMS)之外的所有数据库。NoSQL旨在简化设计、支持横向扩展以及更细致地控制数据的可用性。NoSQL数据库专用于存储特定类型的数据,因此在大多数情况下效率和性能都高于RDBMS服务器。
NoSQL试图放弃关系型数据库的传统结构,让开发人员能够以更接近系统数据流需求的方式实现模型。这意味着NoSQL数据库能够以传统关系型数据库不支持的方式组织数据。
当前有多种不同的NoSQL技术,其中包括HBase列结构、Redis键/值结构以及Virtuoso图结构。然而,本章介绍MongoDB及其使用的文档模型,因为在为Web应用程序和服务实现后端存储方面,它提供了极高的灵活性和可扩展性。另外,MongoDB是当前使用最广泛、得到的支持最多的NoSQL语言。接下来的几小节描述一些NoSQL数据库类型。
文档存储数据库采用面向文档的方法来存储数据,其背后的理念是,可将单个实体的所有数据都存储在一个文档中,而文档可存储在集合中。
文档可包含描述实体的所有必要信息,包括子文档;而在RDBMS中,子文档通常存储为编码字符串或存储在独立的表中。集合中的文档是通过独一无二的键访问的。
最简单的NoSQL数据库是键/值存储。这些数据库存储数据时不采用任何模式(schema),这意味着存储的数据无需遵循任何预定义的结构。键可指向任何数据类型,从对象到字符串值,再到编程语言的函数。
键/值存储的优点是易于实现和添加数据,因此非常适合用于提供基于键来存储和检索数据的简单存储,缺点是无法根据存储的值来查找元素。
列存储数据库在键空间内以列的方式存储数据,其中的键空间基于独一无二的名称、值和时间戳。这类似于键/值数据库,但列存储数据库适合用于存储根据时间戳来区分有效内容和无效内容的数据。这提供了这样的优点,即能够让数据库中存储的数据过期。
图存储数据库是为这样的数据设计的,即能够轻松将其表示为图。这意味着元素通过它们之间的关系相关联,而这些关系的数量是不确定的,就像家谱、社会关系、航线拓扑图或标准交通图那样。
研究NoSQL数据库时,在选择哪种数据库以及如何使用它们方面,应保持开放心态。对于高性能系统尤其应该如此。
您可能选择只使用RDBMS或NoSQL,也可能需要结合使用它们以提供最佳的解决方案。
在所有高性能数据库中,都必须在速度、准确性和可靠性之间进行折衷。下面列出了选择数据库时需要考虑的一些因素。
MongoDB是一种可扩展的敏捷NoSQL数据库,其中的Mongo源自单词humongous。MongoDB基于NoSQL文档存储模型;在这种模型中,数据对象被存储为集合中的文档,而不是传统关系型数据库中的行和列。文档是以二进制JSON(BSON)对象的方式存储的。
MongoDB旨在实现一种高性能、高可用、可自动扩展的数据存储,在本书后面您将看到,它安装和实现起来都非常简单。MongoDB因为速度快、可扩展性强、易于实现,为需要存储用户评论、博客和其他内容的网站提供了极佳的后端存储解决方案。
下面是MongoDB得以成为最受欢迎的NoSQL数据库的其他一些原因。
MongoDB使用集合将数据编组。集合是一组用途相同或类似的文档,相当于传统SQL数据库中的表,但存在一个重要差别:在MongoDB中,集合不受严格模式的管制,其中的文档可根据需要采用稍微不同的结构。这样就无需将文档的内容放在多个不同的表中,而在SQL数据库中经常需要这样做。
在MongoDB数据库中,文档表示单个实体的数据,而集合包含一个或多个相关的文档。MongoDB和SQL的一个主要差别在于文档不同于行:行数据是扁平的,每列都包含行中的一个值,而在MongoDB中,文档可包含嵌入的子文档,提供的数据模型与应用程序的要求更一致。
事实上,MongoDB中表示文档的记录是以BSON(一种轻量级二进制JSON)的方式存储的。BSON是一种轻量级二进制JSON,使用对应于JavaScript属性/值对的字段/值对来定义文档中存储的值。几乎不需要做任何转换,就能将MongoDB记录转换为您可能在应用程序中使用的JSON字符串。
例如,MongoDB中文档的结构可能类似于下面这样,其中包含字段name、version、languages、admin和paths:
{
name: "New Project",
version: 1,
languages: ["JavaScript", "HTML", "CSS"],
admin: {name: "Brad", password: "****"},
paths: {temp: "/tmp", project:"/opt/project", html: "/opt/project/html"}
}
注意到在这个文档结构中,包含类型为字符串、整数、数组和对象的字段/属性,就像JavaScript对象一样。表1.1列出了BSON文档支持的字段值数据类型。
字段名不能包含空格、句点(.)和美元符号($)。另外,字段名_id保留用于存储对象ID(Object ID)。字段_id包含系统中独一无二的ID,这种ID由下列几部分组成:
MongoDB文档最大不能超过16MB,这旨在避免查询占用太多RAM或频繁访问文件系统。文档也许根本不会接近这样的规模,但设计包含文件数据的复杂类型时必须牢记这种最大文档限制。
BSON数据格式提供了多种类型,可用于以二进制方式存储JavaScript对象。这些类型与JavaScript类型非常接近,理解它们很重要,因为查询MongoDB时,您可能要查找指定属性的值为特定类型的对象。例如,您可能在数据库中查询这样的文档:其时间戳为String 对象或Date对象。
MongoDB给每种数据类型都分配了1~255的整数ID号,以方便您按类型查询。表1.1列出了MongoDB支持的数据类型以及MongoDB用来标识它们的编号。
表1.1 MongoDB数据类型及其ID号
类型 |
编号 |
---|---|
Double(双精度浮点数) |
1 |
String(字符串) |
2 |
Object(对象) |
3 |
Array(数组) |
4 |
Binary data(二进制数据) |
5 |
Object ID(对象ID) |
7 |
Boolean(布尔值) |
8 |
Date(日期) |
9 |
Null(空) |
10 |
Regular expression(正则表达式) |
11 |
JavaScript |
13 |
Symbol(符号) |
14 |
JavaScript(带作用域) |
15 |
32-bit integer(32位整数) |
16 |
Timestamp(时间戳) |
17 |
64-bit integer(64位整数) |
18 |
Min key |
255 |
Max key |
127 |
使用MongoDB支持的各种数据类型时,需要注意的另一点是它们的排序顺序。比较不同BSON类型的值时,MongoDB使用下面的排序顺序(从小到大):
1.Min key(内部使用的类型)。
2.Null。
3.数字(32为整数、64位整数和双精度浮点数)。
4.符号和字符串。
5.对象。
6.数组。
7.二进制数据。
8.对象ID。
9.布尔值。
10.日期和时间戳。
11.正则表达式。
12.Max key(内部使用的类型)。
实现MongoDB数据库前,需要了解要存储的数据的性质、如何存储这些数据,以及将如何访问它们。这让您能够预先做出决定,进而通过组织数据和应用程序来获得最佳性能。
具体地说,您需要自问下面的问题:
找到这些问题的答案后,便可以开始考虑MongoDB数据库中集合和文档的结构了。接下来的几小节讨论各种MongoDB建模方法,您可使用它们来为文档、集合和数据库建模以优化数据存储和访问。
数据范式化指的是通过组织文档和集合以最大限度地减少冗余和依赖。为此,可找出这样的对象属性,即属性为子对象,而且应作为一个独立的文档存储在对象文档中的不同集合中。通常这对于这样的对象很有用,即与子对象的关系是一对多或多对多的。
对数据进行范式化的优点是,可减少数据库的规模,因为将只在独立的集合中存储子对象的一个拷贝,而不是在多个对象中重复存储它们。另外,如果需要频繁地修改子对象中的信息,将只需在一个地方修改,而无需在包含它的每个对象中进行修改。
对数据进行范式化的一个重大缺点是,查找对象时如果需要返回子对象,就必须再次查找它。如果需要频繁地访问这些对象,这将严重影响性能。
例如,如果一个系统中的用户都喜欢同一个商店,那么该系统适合对数据进行范式化。表示用户的对象包含属性name、phone和favoriteStore,其中的属性favoriteStore是一个子对象,包含属性name、street、city和zip。
数千位用户可能都喜欢同一个商店,这是一种明显的一对多关系。因此,在每个User对象中都存储FavoriteStore对象不合理,因为这可能导致相同的对象存储数千次。相反,FavoriteStore对象应包含一个_id属性,用于在User对象中引用FavoriteStore对象。这样,应用程序就可在Users集合中使用引用ID favoriteStore关联到FavoriteStores集合中的FavoriteStore文档。
图1.1说明了刚才描述的集合Users和FavoriteStores的结构。
图1.1 添加指向另一个集合中文档的引用,以定义范式化MongoDB文档
对数据进行反范式化指的是找出应直接嵌入到主对象文档中的子对象。这通常适用于这样的情形:主对象和子对象之间为一对一关系或者子对象很少且不会频繁更新。
反范式化文档的主要优点是,只需一次查找就能获得整个对象,而无需在其他集合中查找子对象。这可极大地改善性能。其缺点是,对于与主对象存在一对多关系的子对象,将其存储多个拷贝,这将稍微降低插入速度,还将占用更多的磁盘空间。
一个适合对数据进行反范式化的例子是,系统包含用户的家庭联系信息和工作联系信息。这种用户用包含属性name、home和work的User文档表示,其中属性home和work都是子对象,包含属性phone、street、city和zip。
用户的属性home和work不会频繁变化;多名用户的家庭联系信息可能相同,但这样的情况不会太多。另外,这些子对象存储的值不大,也不会频繁变化。因此,将家庭联系信息直接存储在User对象中是合适的。
属性work需要考虑一下。在您接触的人当中,有多少人的工作联系信息相同呢?如果答案是不多,那么子对象work也应嵌入到User对象中。查询User时,需要获取工作联系信息的频率高吗?如果很少这样做,也许应该将work存储在独立的集合中。然而,如果经常或总是需要这样做,也许应该将其嵌入到User对象中。
图1.2说明了前面描述的内嵌家庭和工作联系信息的User文档的结构。
图1.2 将对象内嵌在文档中,以定义反范式化MongoDB文档
MongoDB的一个优秀特性是,能够创建固定集合。固定集合是大小固定的集合:集合达到指定大小时,如果需要写入新文档,将把最旧的文档删除,再插入新文档。固定集合非常适合用于存储插入、检索和删除频繁的对象。
下面列出了使用固定集合的好处。
固定集合也带来了如下限制。
固定集合非常适合用于存储系统中滚动的事务日志。这让您总是能够访问最后几个日志条目,且不需要显式地删除最旧的条目。
在MongoDB中,写入操作在文档级是原子性的。不能有多个进程同时更新一个文档或集合,这意味着对反范式化文档的写入是原子性的。然而,写入范式化文档时,需要对其他集合中的子文档执行独立的写入操作,因此对范式化文档的写入可能不是原子性的。
设计文档和集合时,必须考虑写入的原子性,以确保设计符合应用程序的要求。换句话说,如果必须将写入对象的各个部分作为一个整体,并确保其原子性,就需要以非范式化方式设计对象。
当您更新文档时,必须考虑其在文档增大方面的影响。MongoDB在文档中提供了一些留白,以支持更新操作导致的典型增大。然而,如果更新导致文档增大到超过了分配给它的磁盘空间,MongoDB就必须将文档移到磁盘的其他位置,而这将影响系统的性能。频繁地移动文档还可能导致磁盘碎片问题。例如,如果文档包含一个数组,而您在这个数组中添加了很多元素,导致文档大小超过了分配给它的空间,就必须将该文档移到磁盘的其他位置。
缓解文档增大问题的方式之一是,对于可能频繁增长的属性,将其设计为范式化对象。例如,不使用数组来存储Cart对象中的商品,而创建一个用于存储商品的CartItems;这样就可以将加入到购物车的商品作为新对象存储到集合CartItems中,并在Cart对象中引用这些商品。
MongoDB提供了多种优化性能、扩展性和可靠性的机制。制定数据库设计方案时,请考虑如下选项。
设计MongoDB文档和集合时,需要考虑的另一个因素是,使用这种设计时将有多少个集合。存在大量集合不会严重影响性能,但一个集合包含大量数据会严重影响性能。对于太大的集合,应想办法将其分成多个。
一个这样的例子是在数据库中存储用户的历史交易记录。您认识到,不需要同时查询多名用户的历史交易记录;保留这些记录只是为了让用户能够查看自己的历史交易。如果有数千名用户,而每位用户都有大量的交易,那么将每位用户的历史交易记录分别存储在一个集合中是合适的。
设计数据库时,最容易忽视的一个方面是数据的生命周期。文档应在集合中存在多久?有些集合包含应永远保留的文档,如活动用户账户。然而,别忘了查询集合时,系统中的每个文档都会带来性能开销。在每个集合中,都应指定文档的存活时间(Time To Live,TTL)。
在MongoDB中实现TTL机制的方式有多种。一种方法是在应用程序中实现对旧数据进行监视和清理的代码;另一种方法对集合设置MongoDB TTL,指定多少秒后或到达指定时间后自动将文档删除。
在只需要最新的文档时,还可实现固定集合来自动限制集合的大小。
需要考虑(甚至反复考虑)的最后一点是数据可用性和性能。对任何Web解决方案来说,这两个方面都是最重要的,因此对提供支持的存储解决方案来说亦如此。
数据可用性指的是数据库能够满足网站的功能需求。您首先需要确保网站能够访问这些数据,这样网站才能正确地运行。用户不会容忍网站不按其指令行事,这也包含数据的准确性。
接下来,需要考虑性能。数据库必须以能够接受的速度提供数据。有关如何评估和设计数据库性能,请参阅前几小节。
在比较复杂的情况下,可能必须先评估数据可用性,再考虑性能,然后回过头去再评估数据可用性,这样循环往复几次,直到找到正确的平衡点。另外别忘了,在当今的世界,可用性需求可能随时发生变化。设计文档和集合时,务必确保它们必要时都能够轻松地扩展。
大多数大型Web应用程序和服务的核心都是高性能的数据存储解决方案。后端数据存储负责存储各种信息,从用户账户信息到购物车中的商品,再到博客和评论。优秀的Web应用程序必须能够准确、快速、可靠地存储和检索数据,因此选择的数据存储机制的性能必须满足用户的需求。
有多种数据存储解决方案可用于存储和检索Web应用程序所需的数据,其中最常见的三种是直接文件系统存储、关系型数据库和NoSQL数据库。本书介绍数据存储解决方案MongoDB,它是一种NoSQL数据库。
本章介绍了决定如何组织数据和配置MongoDB数据库前,必须考虑的设计因素;还介绍了要提出的设计问题以及如何使用MongoDB内置的机制来解决这些问题。
答:有用于Windows、Linux、Mac OS X和Solaris的MongoDB版本。还有企业订阅版本,供要求企业级功能、正常运行时间和支持的专业和商业应用程序使用。如果MongoDB数据对应用程序来说生死攸关,且DB流量非常高,可考虑使用付费的订阅版本。有关这方面的更详细信息,请参阅https://www.mongodb.com/products/mongodb-subscriptions。
答:从某种程度上说有。MongoDB实现动态模式,让您创建集合时无需规定文档结构。这意味着可在同一个集合中存储包含不同字段的文档。
作业包含一组问题及其答案,旨在加深您对本章内容的理解。请尽可能先回答问题,再看答案。
1.范式化文档和反范式化文档有何不同?
2.判断对错:JavaScript是MongoDB文档支持的一种数据类型。
3.固定集合有何用途?
1.反范式化文档包含子文档,而范式化文档的子文档存储在另一个集合中。
2.对。
3.固定集合让您能够对集合的大小或存储的文档数进行限制,从而只保留最新的文档。
1.访问MongoDB文档网站并浏览FAQ网页。这个网页回答了多个与各种主题相关的问题,是个不错的起点。FAQ网页的网址为http://docs.mongodb.org/manual/faq/。
本章介绍如下内容:
本章旨在让您快速熟悉MongoDB。前一章侧重于MongoDB的理论方面,而本章侧重于实际使用。您将学习如何安装MongoDB、启动和停止引擎以及访问MongoDB shell。MongoDB shell让您能够管理MongoDB服务器以及执行各种必要的任务。您将发现,在开发过程中以及管理数据库时,经常需要使用MongoDB shell。
本章介绍如何安装、配置、启动和停止MongoDB数据库;还将介绍MongoDB shell,它让您能够执行从创建用户账户和数据库到实现复制和分片在内的各种MongoDB管理任务。
要开始使用MongoDB,首先需要在您的开发系统中安装它,然后就可以尝试完成各种任务并学习MongoDB shell了。
接下来的几小节将介绍如何安装MongoDB、启动和停止其数据库引擎以及访问MongoDB shell。知道如何执行这些任务后,您便能够开始使用MongoDB了。
实现MongoDB数据库的第一步是安装MongoDB服务器。有用于各种主要平台(Linux、Windows、Solaris和OS X)的MongoDB版本;还有用于Red Hat、SuSE、Ubuntu和Amazon Linux的企业版。MongoDB企业版是基于订阅的,提供了更强大的安全、管理和集成支持。
就本书以及学习MongoDB而言,MongoDB标准版就很好。有关如何下载并安装MongoDB,请参阅http://docs.mongodb.org/manual/installation/。
下面大致介绍了安装和配置过程,本节最后将引导您完成安装和配置。
1.下载MongoDB文件并解压缩。
2.在系统路径中添加<mongo_install_location>/bin。
3.创建一个数据文件目录:<mongo_data_location>/data/db。
4.在控制台提示符下使用下面的命令启动MongoDB。
mongod –dbpath <mongo_data_location>/data/db
安装MongoDB,需要知道如何启动和停止数据库引擎。要启动数据库引擎,可执行<mondo_install_location>/bin中的可执行文件mongod(Windows中为mongod.exe)。这个可执行文件启动MongoDB服务器,并开始在指定端口上侦听数据库请求。
可执行文件mongod接受多个参数,这些参数提供了控制其行为的途径。例如,您可以配置MongoDB在哪个IP地址和端口上侦听,还可配置日志和身份验证。表2.1列出了最常用的参数。
下面的示例在启动MongoDB时指定了参数port和dbpath:
mongod –port 28008 –dbpath <mongo_data_location>/data/db
表2.1 mongod命令行参数
参数 |
描述 |
---|---|
--help,-h |
返回基本的帮助和用法信息 |
--version |
返回MongoDB的版本 |
--config <filename>,-f <filename> |
指定包含运行阶段配置的配置文件 |
--verbose、-v |
增加内部报告的信息量;这些信息被发送到控制台,并被写入到--logpath指定的日志文件 |
--quiet |
减少发送到控制台和日志文件的内部报告的信息量 |
--port <port> |
指定一个TCP端口,mongod将在这个端口上侦听客户端连接。默认为27017 |
--bind_ip <ip address> |
指定mongod将绑定到哪个IP地址以侦听连接。默认为所有接口 |
--maxConns <number> |
指定mongod最多同时接受多少个连接,最多为20000个 |
--logpath <path> |
指定日志文件的路径。重启后将覆盖日志文件,除非指定了--logappend |
--auth |
启用数据库身份验证,对从远程主机连接到数据库的用户进行身份验证 |
--dbpath <path> |
指定一个目录,mongod实例将在其中存储数据 |
--nohttpinterface |
禁用HTTP接口 |
--nojournal |
禁用支持持久性的日记功能(journaling) |
--noprealloc |
禁用数据文件预分配。这将缩短启动时间,但可能严重影响正常操作的性能 |
--repair |
对所有数据库运行修复例程 |
除指定命令行参数外,可执行文件mongod还可接受一个配置文件,其中指定了控制MongoDB服务器行为的配置选项。使用配置文件可更轻松地管理MongoDB配置设置;另外,可创建多个配置文件,供各种数据库角色使用,如开发、测试和生产。
这些配置选项是使用下面的格式指定的,其中<setting>为配置设置,而<value>指定了设置的值:
<setting> = <value>
例如,下面的是一个简单的基本配置文件示例:
verbose = true
port = 27017
dbpath = /data/db
noauth = true
表2.2列出了在配置文件中指定的一些常见配置选项,让您对可指定哪些配置选项有大致了解。
表2.2 mongod配置文件设置
设置 |
描述 |
---|---|
verbose |
增加内部报告的信息量,这些信息将显示到控制台屏幕上或写入到日志文件中。可能取值为true和false。另外,还可使用v、vv、vvv或vvvv来提高详细等级。例如: verbose = true vvv = true |
logpath |
指定将包含MongoDB日志条目的日志文件的位置和文件名 |
logappend |
如果为false,每次启动mongod实例时都将新建一个日志文件,并覆盖旧的日志文件;如果为true,将不会覆盖旧的日志文件,而在它末尾附加。默认为false |
port |
指定一个TCP端口,mongod将在这个端口上侦听客户端连接。默认为27017 |
bind_ip |
指定一个用逗号分隔的IP地址列表,mongod将在这些地址上侦听。默认为所有接口 |
maxConns |
指定MongoDB服务器最多可同时接受多少个连接 |
auth |
如果为true,将启用数据库身份验证,这意味着客户端必须提供身份验证凭证。默认为false |
noauth |
如果为true,将禁用身份验证 |
journal |
如果为true,将启用操作日记,以确保持久性和数据一致性。在64位系统上默认为true,在32位系统上默认为false |
nohttpinterface |
如果为true,将禁用用于访问服务器状态和日志的HTTP接口。默认为false |
rest |
如果为true,将启用MongoDB数据库服务器的简单REST接口,让您能够通过发送REST请求来访问数据库。默认为false |
在开发环境中安装并配置MongoDB
在本节中,您将在开发环境中实现MongoDB。继续往下阅读前,务必完成本节介绍的步骤,确保在您的开发环境中正确地配置了MongoDB。
请按如下步骤在您的开发系统中安装并配置MongoDB。
1.前往www.mongodb.org/downloads,根据您的系统下载相应的MongoDB生产版本。
2.将文件解压缩到要运行MongoDB的位置(以下称之为<mongo_install_path>)。
3.在系统路径中添加<mongo_install_location>/bin。
4.创建一个数据文件目录:<mongo_install_location>/data/db。
5.创建配置文件<mongo_install_location>/bin/mongod_config.txt。
6.在该配置文件中添加如下配置设置并存盘:
7.启动MongoDB服务器。打开一个控制台窗口,并在其中使用下面的命令启动;您需要将<mongo_data_location>替换为您的安装目录。这将启动MongoDB数据库服务器。
8.启动MongoDB shell。再打开一个控制台窗口,并执行命令mongo来启动MongoDB shell。
9.执行下面的命令以停止MongoDB服务器:
10.执行命令exit退出MongoDB shell。
至此,您成功地安装、配置、启动和停止了MongoDB服务器。
▲
启动可执行文件mongod后,停止它的方法随平台而异。然而,停止它的最佳方法是在MongoDB shell中进行,这将干净地终止当前操作,并强制mongod退出。
要在MongoDB shell中停止MongoDB数据库服务器,可使用下面的命令,切换到admin数据库再关闭数据库引擎:
use admin
db.shutdownServer()
▼ Try It Yourself
verbose = true
port = 27017
dbpath=c:\mongodb\data\db\
noauth = true
maxConns = 10
rest = true
mongod --config <mongo_install_location>/bin/mongod_config.txt
use admin
db.shutdownServer()
MongoDB内置了一个HTTP接口,可向您提供有关MongoDB服务器的信息。HTTP接口提供了有关MongoDB服务器的状态信息,还提供了一个REST接口,让您能够通过REST调用来访问数据库。
在大多数情况下,您都将在应用程序中使用编程语言专用的驱动程序来访问MongoDB数据库。然而,使用HTTP接口通常有助于获悉如下信息。
要访问MongoDB HTTP接口,可访问该接口的端口28017。
例如,在启动了MongoDB服务器的情况下,在本地主机上使用下面的URL可访问MongoDB HTTP接口(如图2.1所示):http://localhost:28017/。
图2.1 使用MongoDB HTTP接口在浏览器中查看MongoDB数据库信息
安装、配置并启动MongoDB后,便可通过MongoDB shell访问它了。MongoDB shell是MongoDB自带的一个交互式JavaScript shell,让您能够访问、配置和管理MongoDB数据库、用户等。使用这个shell可执行各种任务,从设置用户账户到创建数据库,再到查询数据库内容,无所不包。
接下来的几小节将带您完成在MongoDB shell中执行的一些常见管理任务。具体地说,您需要能够创建用户账户、数据库和集合,才能完成本书后面的示例。您至少还应能够执行基本的文档查询,以帮助排除数据访问故障。
MongoDB shell是一个可执行文件,位于MongoDB安装路径下的/bin文件夹中。要启动MongoDB shell,可执行命令mongo。这将在控制台提示符中启动该shell,如图2.2所示。
图2.2 启动MongoDB shell
启动MongoDB shell后,就可通过它管理MongoDB的各个方面。使用MongoDB shell时,别忘了它是基于JavaScript的。这意味着您可使用大部分JavaScript语法(包括循环和函数)来与数据库交互。
MongoDB shell提供了多个命令,您可在shell提示符下执行它们。您需要熟悉这些命令,因为您将经常使用它们。表2.3列出了多个MongoDB shell命令及其用途。
表2.3 MongoDB shell命令
命令 |
描述 |
---|---|
help <option> |
显示MongoDB shell命令的语法帮助。参数option让您能够指定需要哪方面的帮助,如db、collection或cursor |
use <database> |
修改当前数据库句柄。数据库操作是在当前数据库句柄上进行的 |
show <option> |
根据参数option显示一个列表。参数option的可能取值如下。 dbs:显示数据库列表 collections:显示当前数据库中的集合列表 users:显示当前数据库中的用户列表 profile:显示system.profile中时间超过1毫秒的条目 |
log [name] |
显示内存中日志的最后一部分。如果没有指定日志名,则默认为global |
exit |
退出MongoDB shell |
MongoDB shell提供了用于执行管理任务的原生方法,您可在MongoDB shell中直接调用它们,也可在MongoDB shell中执行的脚本中调用它们。
包括DB、Collection和Cursor在内的JavaScript对象也提供了管理方法,这将在本书后面讨论。
表2.4列出了最常见的原生方法,它们提供了建立连接、创建对象、加载脚本等功能。
表2.4 MongoDB shell原生方法和构造函数
方法 |
描述 |
---|---|
Date() |
创建一个Date对象。默认情况下,创建一个包含当前日期的Date对象 |
UUID(hex_string) |
将32字节的十六进制字符串转换为BSON子类型UUID |
ObjectId.valueOf() |
将一个ObjectId的属性str显示为十六进制字符串 |
Mongo.getDB(database) |
返回一个数据库对象,它表示指定的数据库 |
Mongo(host:port) |
创建一个连接对象,它连接到指定的主机和端口 |
connect(string) |
连接到指定MongoDB实例中的指定数据库。返回一个数据库对象。连接字符串的格式如下:host:port/database,如db = connect("localhost:28001/myDb") |
cat(path) |
返回指定文件的内容 |
version() |
返回当前MongoDB shell实例的版本 |
cd(path) |
将工作目录切换到指定路径 |
getMemInfo() |
返回一个文档,指出了MongoDB shell当前占用的内存量 |
hostname() |
返回运行MongoDB shell的系统的主机名 |
_isWindows() |
如果MongoDB shell运行在Windows系统上,就返回true;如果运行在UNIX或Linux系统上,就返回false |
load(path) |
在MongoDB shell中加载并运行参数path指定的JavaScript文件 |
_rand() |
返回一个0~1的随机数 |
MongoDB shell是一个交互式JavaScript shell,与MongoDB数据结构联系紧密。这意味着大部分数据交互(从传递给方法的参数到从方法返回的数据)都是标准的MongoDB文档——在大多数情况下都是JavaScript对象。
例如,创建用户时,传入一个类似于下面的文档来定义用户:
db.addUser( { user: "testUser",
userSource: "test",
roles: [ "read" ],
otherDBRoles: { testDB2: [ "readWrite" ] } } )
在MongoDB shell中列出数据库的用户时,以类似于下面的文档列表显示用户:
> db.system.users.find()
{ "_id" : ObjectId("529e71927c798d1dd56a63d9"),
"user" : "dbadmin",
"pwd" : "78384f4d73368bd2d3a3e1da926dd269",
"roles" : [ "readWriteAnyDatabase", "dbAdminAnyDatabase", "clusterAdmin" ]
}
{ "_id" : ObjectId("52a098861db41f82f6e3d489"),
"user" : "useradmin",
"pwd" : "0b4568ab22a52a6b494fd54e64fcee9f",
"roles" : [ "userAdminAnyDatabase" ]
}
正如您看到的,MongoDB shell命令、方法和数据结构都是基于交互式JavaScript的。为管理MongoDB,一种很不错的方法是创建脚本,这些脚本可运行多次,也可在指定的时间(如升级时)运行。
在脚本文件中,可包含任意数量使用JavaScript(如条件语句和循环)的MongoDB命令。MongoDB shell脚本编程主要是通过三种方式实现的。
接下来的几小节详细介绍这些方式。
一种以脚本方式执行MongoDB shell命令的方法是,使用命令行选项--eval。参数--eval接受一个JavaScript字符串或JavaScript文件,启动MongoDB shell,并立即执行这些JavaScript代码。除--eval外,您还可使用其他命令行参数指定配置文件、数据库、端口、身份验证信息等。
例如,下面的命令启动MongoDB shell,连接到数据库test,对该数据库执行db.getCollections(),并以JSON字符串的方式输出结果:
mongo test --eval "printjson(db.getCollectionNames())"
上述命令的输出类似于下面这样:
C:\Users\Brad>mongo words --eval "printjson(db.getCollectionNames())"
MongoDB shell version: 2.4.8
connecting to: words
[
"system.indexes",
"word_stats",
"word_stats2",
"word_stats_new",
"words"
]
还可以在MondoDB shell提示符下使用方法load(script_ path)来执行JavaScript文件。这个方法加载并立即执行指定的JavaScript文件,例如,下面的MondoDB shell加载并执行脚本文件db_update.js:
load("/data/db/get_collections.js")
MongoDB shell 脚本编程
在本节中,您将创建自己的MongoDB shell脚本。本书后面要求您能够创建JavaScript文件,并在其中使用MongoDB shell命令连接到数据库、访问集合及执行各种任务。
请按下面的步骤创建一个JavaScript文件,它使用MongoDB shell命令输出一些信息。
1.创建一个名为code的文件夹,用于存储本书的示例代码。
2.在文件夹code中,创建一个名为hour02的文件夹。
3.在文件夹code/hour02中,创建文件shell_script.js。
4.将程序清单2.1所示的代码复制到这个文件中。这些代码包含一些基本的MongoDB shell命令,并使用print()和printjson()来输出结果。
5.将这个文件存盘。
6.启动MongoDB服务器。
7.再打开一个控制台窗口,并切换到目录code/hour02。
8.执行命令mongo shell_script.js来运行这个脚本,您将看到类似于程序清单2.2的输出。
9.使用命令mongo启动MongoDB shell。
10.在MongoDB shell中,使用命令load("shell_script.js")再次执行这个脚本。
输出应与您在第8步看到的相同。
程序清单2.1 shell_script.js:执行MongoDB shell命令并输出结果的JavaScript文件
程序清单2.2 shell_script.js-output:执行MongoDB shell命令的JavaScript文件的输出
▲
最常用的MongoDB shell脚本编程方法是,创建一个JavaScript文件,并直接使用命令mongo来执行它。MongoDB shell将读取JavaScript文件,逐行执行并在执行完毕后退出。
要在执行JavaScript代码时将结果输出到控制台,可使用函数print()。您还可使用MongoDB shell特有的函数printjson(),它自动设置JSON对象的格式。然而printjson()采用的格式占用的屏幕空间太多,最好使用JavaScript方法(如JSON.stringify())来设置输出JSON对象的格式。
例如,假设您在文件get_collections.js中添加了如下JavaScript代码:
db = connect("localhost/words");
printjson(db.getCollectionNames())
可直接在控制台中使用下面的mongo命令来执行这个脚本:
mongo get_collections.js
这将加载该脚本、连接到数据库words并显示其中的集合。输出类似于下面这样:
MongoDB shell version: 2.4.8
connecting to: test
type "help" for help
connecting to: localhost/words
[
"system.indexes",
"word_stats",
"word_stats2",
"word_stats_new",
"words"
]
还可执行多个JavaScript文件,为此只需在命令mongo中用空格分隔它们,如下所示:
mongo get_ collections.js load_data.js get_data.js
▼ Try It Yourself
01 print("Hostname:");
02 print("\t"+hostname());
03 print("Date:");
04 print("\t"+Date());
05 db = connect("localhost/admin");
06 print("Admin Collections:");
07 printjson(db.getCollectionNames());
Hostname:
MyComputer
Date:
Mon Jan 20 2014 13:10:36 GMT-0700 (Mountain Standard Time)
connecting to: localhost/admin
Admin Collections:
[ "fs.chunks", "fs.files", "system.indexes", "system.users" ]
本章旨在让您快速熟悉MongoDB服务器和MongoDB shell。您下载并安装了MongoDB,学习了如何设置配置选项,以控制MongoDB服务器的行为。
您还学习了如何启动MongoDB shell。MongoDB shell是一个用于同MongoDB交互的JavaScript界面,您可在其中执行方法,以访问MongoDB服务器、查看集合和数据库以及执行管理任务。
您还学习了如何编写并执行这样的JavaScript文件:使用MongoDB shell命令来执行数据库操作。
答:有。每种平台都有其启动和停止MongoDB服务器的方法。例如,在Windows、Linux和Mac平台中,可将MongoDB服务器配置成服务,再使用随平台而异的方法来启动和停止MongoDB服务器。
答:可以。为此,可使用上、下箭头键。
作业包含一组问题及其答案,旨在加深您对本章内容的理解。请尽可能先回答问题,再看答案。
1.命令行选项--eval有何用途?
2.在MongoDB shell中如何显示数据库清单?
3.如何干净地关闭MongoDB服务器?
4.指定配置时,使用命令行选项更好还是使用配置文件更好?
1.命令行选项--eval在MongoDB shell中执行指定的JavaScript表达式、输出结果并退出MongoDB shell。
2.执行命令show dbs。
3.在MongoDB shell执行下面的命令,它们切换到数据库admin并关闭服务器:
use admin
db.shutdownServer()
4.通常使用配置文件更好,因为可轻松地修改和调整其中的设置。另外,还可创建多个配置文件,用于各种DB角色。
1.在文件夹code/hour02中,创建一个名为test.js的JavaScript文件,在其中添加如下命令,并在命令行使用mongo test.js来执行它:
print(hostname());
print(Date());
2.启动MongoDB shell,在其中调用方法load("./test.js")来执行前一个练习中编写的文件。
本章介绍如下内容:
使用Python对大型数据集分页;
使用Python限制从文档中返回的字段;
使用Python生成文档中不同字段值列表;
使用Python对文档进行分组并生成返回数据集;
在Python应用程序中使用聚合流水线根据集合中的文档生成数据集。
本章继续介绍Python MongoDB驱动程序,以及如何在Python应用程序中使用它来检索数据,重点是限制返回的结果,这是通过限制返回的文档数和字段以及对大型数据集进行分页实现的。
本章还将介绍如何在Python应用程序执行各种分组和聚合操作。这些操作让您能够在服务器端处理数据,再将结果返回给Python应用程序,从而减少发送的数据量以及应用程序的工作量。
在大型系统上查询较复杂的文档时,常常需要限制返回的内容,以降低对服务器和客户端网络和内存的影响。要限制与查询匹配的结果集,方法有三种:只接受一定数量的文档;限制返回的字段;对结果分页,分批地获取它们。
使用将对象表示的文档减少到指定的数量
在本节中,您将编写一个简单的Python应用程序,它使用limit()来限制find()操作返回的结果。通过这个示例,您将熟悉如何结合使用limit()和find(),并了解limit()对结果的影响。程序清单17.1显示了这个示例的代码。
在这个示例中,函数__main__连接到MongoDB数据库,获取一个Collection对象,并调用其他方法来查找并显示数量有限的文档。方法displayCursor()迭代游标并显示找到的单词。
方法limitResults()接受一个limit参数,查找以p打头的单词,并返回参数limit指定的单词数。
请执行如下步骤,创建并运行这个Python应用程序,它在示例数据集中查找指定数量的文档并显示结果。
1.确保启动了MongoDB服务器。
2.确保下载并安装了Python MongoDB驱动程序,并运行了生成数据库words的脚本文件code/hour05/generate_words.js。
3.在文件夹code/hour17中新建一个文件,并将其命名为PythonFindLimit.py。
4.在这个文件中输入程序清单17.1所示的代码。这些代码使用了方法find()和limit()。
5.将这个文件存盘。
6.打开一个控制台窗口,并切换到目录code/hour17。
7.执行下面的命令来运行这个Python应用程序。程序清单17.2显示了这个应用程序的输出。
程序清单17.1 PythonFindLimit.py:在Python应用程序中在集合中查找指定数量的文档
程序清单17.2 PythonFindLimit.py-output:在Python应用程序中在集合中查找指定数量文档的输出
▲
要限制find()或其他查询请求返回的数据量,最简单的方法是对find()操作返回的Cursor对象调用方法limit(),它让Cursor对象返回指定数量的文档,可避免检索的对象量超过应用程序的处理能力。
例如,下面的代码只显示集合中的前10个文档,即便匹配的文档有数千个:
cursor = wordsColl.find()
cursor.limit(10)
for word in cursor:
print (word)
▼ Try It Yourself
python PythonFindLimit.py
01 from pymongo import MongoClient
02 def displayCursor(cursor):
03 words = ''
04 for doc in cursor:
05 words += doc["word"] + ","
06 if len(words) > 65:
07 words = words[:65] + "..."
08 print (words)
09 def limitResults(collection, limit):
10 query = {'first': 'p'}
11 cursor = collection.find(query)
12 cursor.limit(limit)
13 print ("\nP words Limited to " + str(limit) +" :")
14 displayCursor(cursor)
15 if __name__=="__main__":
16 mongo = MongoClient('mongodb://localhost:27017/')
17 db = mongo['words']
18 collection = db['word_stats']
19 limitResults(collection, 1)
20 limitResults(collection, 3)
21 limitResults(collection, 5)
22 limitResults(collection, 7)
P words Limited to 1 :
people,
P words Limited to 3 :
people,put,problem,
P words Limited to 5 :
people,put,problem,part,place,
P words Limited to 7 :
people,put,problem,part,place,program,play,
在方法中使用参数来减少对象表示的文档中的字段数
在本节中,您将编写一个简单的Python应用程序,它在方法find()中使用参数fields来限制返回的字段。通过这个示例,您将熟悉如何使用方法find()的参数fields,并了解它对结果的影响。程序清单17.3显示了这个示例的代码。
在这个示例中,函数__main__连接到MongoDB数据库,获取一个Collection对象,并调用其他的方法来查找文档并显示其指定的字段。方法displayCursor()迭代游标并显示找到的文档。
方法includeFields()接受一个字段名列表,创建参数fields并将其传递给方法find(),使其只返回指定的字段;方法excludeFields()接受一个字段名列表,创建参数fields并将其传递给方法find(),以排除指定的字段。
请执行如下步骤,创建并运行这个Python应用程序,它在示例数据集中查找文档、限制返回的字段并显示结果。
1.确保启动了MongoDB服务器。
2.确保下载并安装了Python MongoDB驱动程序,并运行了生成数据库words的脚本文件code/hour05/generate_words.js。
3.在文件夹code/hour17中新建一个文件,并将其命名为PythonFindFields.py。
4.在这个文件中输入程序清单17.3所示的代码。这些代码在调用方法find()时传递了参数fields。
5.将这个文件存盘。
6.打开一个控制台窗口,并切换到目录code/hour17。
7.执行下面的命令来运行这个Python应用程序。程序清单17.4显示了这个应用程序的输出。
程序清单17.3 PythonFindFields.py:在Python应用程序中限制从集合返回的文档包含的字段
程序清单17.4 PythonFindFields.py-output:在Python应用程序中限制从集合返回的文档包含的字段的输出
▲
为限制文档检索时返回的数据量,另一种极有效的方式是限制要返回的字段。文档可能有很多字段在有些情况下很有用,但在其他情况下没用。从MongoDB服务器检索文档时,需考虑应包含哪些字段,并只请求必要的字段。
要对Collection对象的方法find()从服务器返回的字段进行限制,可使用参数fields。这个参数是一个Dictionary对象,它使用值True来包含字段,使用值False来排除字段。
例如,要在返回文档时排除字段stats、value和comments,可使用下面的fields参数:
fields = {'stats' : false, 'value' : false, 'comments' : False);
cursor = myColl.find(None, fields)
这里将查询对象指定成了None,因为您要查找所有的文档。
仅包含所需的字段通常更容易。例如,如果只想返回first字段为t的文档的word和size字段,可使用下面的代码:
query = {'first' : 't'}
fields = {'word' : true, 'size' : True}
cursor = myColl.find(query, fields)
▼ Try It Yourself
python PythonFindFields.py
01 from pymongo import MongoClient
02 def displayCursor(cursor):
03 print (cursor)
04 def includeFields(collection, fields):
05 query = {'first': 'p'}
06 fieldObj = {}
07 for field in fields:
08 fieldObj[field] = True
09 word = collection.find_one(query, fieldObj)
10 print ("\nIncluding " + str(fields) +" fields:")
11 displayCursor(word)
12 def excludeFields(collection, fields):
13 query = {'first': 'p'}
14 if not len(fields):
15 fieldObj = None
16 else:
17 fieldObj = {}
18 for field in fields:
19 fieldObj[field] = False
20 doc = collection.find_one(query, fieldObj)
21 print ("\nExcluding " + str(fields) + " fields:")
22 displayCursor(doc)
23 if __name__=="__main__":
24 mongo = MongoClient('mongodb://localhost:27017/')
25 db = mongo['words']
26 collection = db['word_stats']
27 excludeFields(collection, [])
28 includeFields(collection, ['word', 'size'])
29 includeFields(collection, ['word', 'letters'])
30 excludeFields(collection, ['letters', 'stats', 'charsets'])
Excluding [] fields:
{ 'stats': {'consonants': 3.0, 'vowels': 3.0}, 'last': 'e',
'charsets': [{'chars': ['p', 'l'], 'type': 'consonants'},
{'chars': ['e', 'o'], 'type': 'vowels'}],
'first': 'p', 'letters': ['p', 'e', 'o', 'l'], 'word': 'people',
'_id': ObjectId('52e89477c25e849855325fa7'), 'size': 6.0}
Including ['word', 'size'] fields:
{'_id': ObjectId('52e89477c25e849855325fa7'), 'word': 'people', 'size': 6.0}
Including ['word', 'letters'] fields:
{ 'letters': ['p', 'e', 'o', 'l'],
'_id': ObjectId('52e89477c25e849855325fa7'), 'word': 'people'}
Excluding ['letters', 'stats', 'charsets'] fields:
{ 'last': 'e', 'first': 'p', 'letters': ['p', 'e', 'o', 'l'],
'word': 'people', '_id': ObjectId('52e89477c25e849855325fa7'), 'size': 6.0}
在Python中使用skip()和limit()对MongoDB集合中的文档进行分页
在本节中,您将编写一个简单的Python应用程序,它使用Cursor对象的方法skip()和limit()方法对find()返回的大量文档进行分页。通过这个示例,您将熟悉如何使用skip()和limit()对较大的数据集进行分页。程序清单17.5显示了这个示例的代码。
在这个示例中,函数__main__连接到MongoDB数据库,获取一个Collection对象,并调用其他的方法来查找文档并以分页方式显示它们。方法displayCursor()迭代游标并显示当前页中的单词。
方法pageResults()接受一个skip参数,并根据它以分页方式显示以w开头的所有单词。每显示一页后,都将skip值递增,直到到达游标末尾。
请执行下面的步骤,创建并运行这个对示例数据集中的文档进行分页并显示结果的Python应用程序。
1.确保启动了MongoDB服务器。
2.确保下载并安装了Python MongoDB驱动程序,并运行了生成数据库words的脚本文件code/hour05/generate_words.js。
3.在文件夹code/hour17中新建一个文件,并将其命名为PythonFindPaging.py。
4.在这个文件中输入程序清单17.5所示的代码。这些代码实现了文档集分页。
5.将这个文件存盘。
6.打开一个控制台窗口,并切换到目录code/hour17。
7.执行下面的命令来运行这个Python应用程序。程序清单17.6显示了这个应用程序的输出。
程序清单17.5 PythonFindPaging.py:在Python应用程序中分页显示集合中的文档集
程序清单17.6 PythonFindPaging.py-output:在Python应用程序中分页显示集合中的文档集的输出
▲
为减少返回的文档数,一种常见的方法是进行分页。要进行分页,需要指定要在结果集中跳过的文档数,还需限制返回的文档数。跳过的文档数将不断增加,每次的增量都是前一次返回的文档数。
要对一组文档进行分页,需要使用Cursor对象的方法limit()和skip()。方法skip()让您能够指定在返回文档前要跳过多少个文档。
每次获取下一组文档时,都增大方法skip()中指定的值,增量为前一次调用limit()时指定的值,这样就实现了数据集分页。
例如,下面的语句查找第11~20个文档:
cursor = collection.find()
cursor.limit(10)
cursor.skip(10)
进行分页时,务必调用方法sort()来确保文档的排列顺序不变。
▼ Try It Yourself
python PythonFindPaging.py
01 from pymongo import MongoClient
02 def displayCursor(cursor):
03 words = ''
04 for doc in cursor:
05 words += doc["word"] + ","
06 if len(words) > 65:
07 words = words[:65] + "..."
08 print (words)
09 def pageResults(collection, skip):
10 query = {'first': 'w'}
11 cursor = collection.find(query)
12 cursor.limit(10)
13 cursor.skip(skip)
14 print ("Page " + str(skip+1) + " to " + \
15 str(skip + cursor.count(True)) + ":")
16 displayCursor(cursor);
17 if(cursor.count(True) == 10):
18 pageResults(collection, skip+10);
19 if __name__=="__main__":
20 mongo = MongoClient('mongodb://localhost:27017/')
21 db = mongo['words']
22 collection = db['word_stats']
23 pageResults(collection, 0)
Page 1 to 10:
with,won't,we,what,who,would,will,when,which,want,
Page 11 to 20:
way,well,woman,work,world,while,why,where,week,without,
Page 21 to 30:
water,write,word,white,whether,watch,war,within,walk,win,
Page 31 to 40:
wait,wife,whole,wear,whose,wall,worker,window,wrong,west,
Page 41 to 50:
whatever,wonder,weapon,wide,weight,worry,writer,whom,wish,western...
Page 51 to 60:
wind,weekend,wood,winter,willing,wild,worth,warm,wave,wonderful,
Page 61 to 70:
wine,writing,welcome,weather,works,wake,warn,wing,winner,welfare,
Page 71 to 80:
witness,waste,wheel,weak,wrap,warning,wash,widely,wedding,wheneve...
Page 81 to 90:
wire,whisper,wet,weigh,wooden,wealth,wage,wipe,whereas,withdraw,
Page 91 to 93:
working,wisdom,wealthy,
使用Python检索一组文档中指定字段的不同值
在本节中,您将编写一个Python应用程序,它使用Collection对象的方法distinct()来检索示例数据库中不同的字段值。通过这个示例,您将熟练地生成数据集中的不同字段值列表。程序清单17.7显示了这个示例的代码。
在这个示例中,函数__main__连接到MongoDB数据库,获取一个Collection对象,并调用其他的方法来找出并显示不同的字段值。
方法sizesOfAllWords()找出并显示所有单词的各种长度;方法sizesOfQWords()找出并显示以q打头的单词的各种长度;方法firstLetterOfLongWords()找出并显示长度超过12的单词的各种长度。
请执行下面的步骤,创建并运行这个Python应用程序,它找出示例数据集中文档集的不同字段值,并显示结果。
1.确保启动了MongoDB服务器。
2.确保下载并安装了Python MongoDB驱动程序,并运行了生成数据库words的脚本文件code/hour05/generate_words.js。
3.在文件夹code/hour17中新建一个文件,并将其命名为PythonFindDistinct.py。
4.在这个文件中输入程序清单17.7所示的代码。这些代码对文档集执行distinct()操作。
5.将这个文件存盘。
6.打开一个控制台窗口,并切换到目录code/hour17。
7.执行下面的命令来运行这个Python应用程序。程序清单17.8显示了这个应用程序的输出。
程序清单17.7 PythonFindDistinct.py:在Python应用程序中找出文档集中不同的字段值
程序清单17.8 PythonFindDistinct.py-output:在Python应用程序中找出文档集中不同字段值的输出
▲
一种很有用的MongoDB集合查询是,获取一组文档中某个字段的不同值列表。不同(distinct)意味着纵然有数千个文档,您只想知道那些独一无二的值。
Collection和Cursor对象的方法distinct()让您能够找出指定字段的不同值列表,这个方法的语法如下:
distinct(key)
其中参数key是一个字符串,指定了要获取哪个字段的不同值。要获取子文档中字段的不同值,可使用句点语法,如stats.count。如果要获取部分文档中指定字段的不同值,可先使用查询生成一个Cursor对象,再对这个Cursor对象调用方法distinct()。
例如,假设有一些包含字段first、last和age的用户文档,要获取年龄超过65岁的用户的不同姓,可使用下面的操作:
query = {'age' : {'$gt' : 65}}
cursor = myCollection.find(query)
lastNames = cursor.distinct('last')
方法distinct()返回一个数组,其中包含指定字段的不同值,例如:
["Smith", "Jones", ...]
▼ Try It Yourself
python PythonFindDistinct.py
01 from pymongo import MongoClient
02 def sizesOfAllWords(collection):
03 results = collection.distinct("size")
04 print ("\nDistinct Sizes of words: ")
05 print (str(results))
06 def sizesOfQWords(collection):
07 query = {'first': 'q'}
08 cursor = collection.find(query)
09 results = cursor.distinct("size")
10 print ("\nDistinct Sizes of words starting with Q:")
11 print (str(results))
12 def firstLetterOfLongWords(collection):
13 query = {'size': {'$gt': 12}}
14 cursor = collection.find(query)
15 results = cursor.distinct("first")
16 print ("\nDistinct first letters of words longer than" + \
17 " 12 characters:")
18 print (str(results))
19 if __name__=="__main__":
20 mongo = MongoClient('mongodb://localhost:27017/')
21 db = mongo['words']
22 collection = db['word_stats']
23 sizesOfAllWords(collection)
24 sizesOfQWords(collection)
25 firstLetterOfLongWords(collection)
Distinct Sizes of words:
[3.0, 2.0, 1.0, 4.0, 5.0, 9.0, 6.0, 7.0, 8.0, 10.0, 11.0, 12.0, 13.0, 14.0]
Distinct Sizes of words starting with Q:
[8.0, 5.0, 7.0, 4.0]
Distinct first letters of words longer than 12 characters:
['i', 'a', 'e', 'r', 'c', 'u', 's', 'p', 't']
使用Python根据键值将文档分组
在本节中,您将创建一个简单的Python应用程序,它使用Collection对象的方法group()从示例数据库检索文档,根据指定字段进行分组,并在服务器上执行reduce和finalize函数。通过这个示例,您将熟悉如何使用group()在服务器端对数据集进行处理,以生成分组数据。程序清单17.9显示了这个示例的代码。
在这个示例中,函数__main__连接到MongoDB数据库,获取一个Collection对象,并调用其他的方法来查找文档、进行分组并显示结果。方法displayGroup()显示分组结果。
方法firstIsALastIsVowel()将第一个字母为a且最后一个字母为元音字母的单词分组,其中的reduce函数计算单词数,以确定每组的单词数。
方法firstLetterTotals()根据第一个字母分组,并计算各组中所有单词的元音字母总数和辅音字母总数。在其中的finalize函数中,将元音字母总数和辅音字母总数相加,以提供各组单词的字符总数。
请执行下面的步骤,创建并运行这个Python应用程序,它对示例数据集中的文档进行分组和处理,并显示结果。
1.确保启动了MongoDB服务器。
2.确保下载并安装了Python MongoDB驱动程序,并运行了生成数据库words的脚本文件code/hour05/generate_words.js。
3.在文件夹code/hour17中新建一个文件,并将其命名为PythonGroup.py。
4.在这个文件中输入程序清单17.9所示的代码。这些代码对文档集执行group()操作。
5.将这个文件存盘。
6.打开一个控制台窗口,并切换到目录code/hour17。
7.执行下面的命令来运行这个Python应用程序。程序清单17.10显示了这个应用程序的输出。
程序清单17.9 PythonGroup.py:在Python应用程序中根据字段值对单词分组以生成不同的数据
程序清单17.10 PythonGroup.py-output:在Python应用程序中根据字段值对单词分组以生成不同数据的输出
▲
在Python中对大型数据集执行操作时,根据文档的一个或多个字段的值将结果分组通常很有用。这也可以在取回文档后使用代码来完成,但让MongoDB服务器在原本就要迭代文档的请求中这样做,效率要高得多。
在Python中,要将查询结果分组,可使用Collection对象的方法group()。分组请求首先收集所有与查询匹配的文档,再对于指定键的每个不同值,都在数组中添加一个分组对象,对这些分组对象执行操作,并返回这个分组对象数组。
方法group()的语法如下:
group({key, cond , initial, reduce, [finalize]})
其中参数key、cond和initial都是Dictionary对象,指定了要用来分组的字段、查询以及要使用的初始文档;参数reduce和finalize为String对象,包含以字符串方式表示的JavaScript函数,这些函数将在服务器上运行以归并文档并生成最终结果。有关这些参数的更详细信息,请参阅第9章。
为演示这个方法,下面的代码实现了简单分组,它创建了对象key、cond和initial,并以字符串的方式传入了一个reduce函数:
key = {'first' : True }
cond = {'first' : 'a', 'size': 5}
initial = {'count' : 0}
reduce = "function (obj, prev) { prev.count++; }"
results = collection.group(key, cond, initial, reduce)
方法group()返回一个包含分组结果的List。下面的代码逐项地显示了分组结果的内容:
for result in results:
print (result)
▼ Try It Yourself
python PythonGroup.py
01 from pymongo import MongoClient
02 def displayGroup(results):
03 for result in results:
04 print (result)
05 def firstIsALastIsVowel(collection):
06 key = {'first' : True, "last" : True}
07 cond = {'first' : 'a', 'last' :
08 {'$in' : ["a","e","i","o","u"]}}
09 initial = {'count' : 0}
10 reduce = "function (obj, prev) { prev.count++; }"
11 results = collection.group(key, cond, initial, reduce)
12 print ("\n\n'A' words grouped by first and last" + \
13 " letter that end with a vowel:")
14 displayGroup(results)
15 def firstLetterTotals(collection):
16 key = {'first' : True}
17 cond = {}
18 initial = {'vowels' : 0, 'cons' : 0}
19 reduce = "function (obj, prev) { " + \
20 "prev.vowels += obj.stats.vowels; " + \
21 "prev.cons += obj.stats.consonants; " + \
22 "}"
23 finalize = "function (obj) { " + \
24 "obj.total = obj.vowels + obj.cons; " + \
25 "}"
26 results = collection.group(key, cond, initial, reduce, finalize)
27 print ("\n\nWords grouped by first letter " + \
28 "with totals:")
29 displayGroup(results)
30 if __name__=="__main__":
31 mongo = MongoClient('mongodb://localhost:27017/')
32 db = mongo['words']
33 collection = db['word_stats']
34 firstIsALastIsVowel(collection)
35 firstLetterTotals(collection)
'A' words grouped by first and last letter that end with a vowel:
{'count': 3.0, 'last': 'a', 'first': 'a'}
{'count': 2.0, 'last': 'o', 'first': 'a'}
{'count': 52.0, 'last': 'e', 'first': 'a'}
Words grouped by first letter with totals:
{'total': 947.0, 'cons': 614.0, 'vowels': 333.0, 'first': 't'}
{'total': 690.0, 'cons': 444.0, 'vowels': 246.0, 'first': 'b'}
{'total': 1270.0, 'cons': 725.0, 'vowels': 545.0, 'first': 'a'}
{'total': 441.0, 'cons': 237.0, 'vowels': 204.0, 'first': 'o'}
{'total': 906.0, 'cons': 522.0, 'vowels': 384.0, 'first': 'i'}
{'total': 393.0, 'cons': 248.0, 'vowels': 145.0, 'first': 'h'}
{'total': 701.0, 'cons': 443.0, 'vowels': 258.0, 'first': 'f'}
{'total': 67.0, 'cons': 41.0, 'vowels': 26.0, 'first': 'y'}
{'total': 474.0, 'cons': 313.0, 'vowels': 161.0, 'first': 'w'}
{'total': 947.0, 'cons': 585.0, 'vowels': 362.0, 'first': 'd'}
{'total': 1946.0, 'cons': 1233.0, 'vowels': 713.0, 'first': 'c'}
{'total': 1855.0, 'cons': 1215.0, 'vowels': 640.0, 'first': 's'}
{'total': 344.0, 'cons': 208.0, 'vowels': 136.0, 'first': 'n'}
{'total': 374.0, 'cons': 240.0, 'vowels': 134.0, 'first': 'g'}
{'total': 679.0, 'cons': 417.0, 'vowels': 262.0, 'first': 'm'}
{'total': 70.0, 'cons': 48.0, 'vowels': 22.0, 'first': 'k'}
{'total': 210.0, 'cons': 117.0, 'vowels': 93.0, 'first': 'u'}
{'total': 1514.0, 'cons': 964.0, 'vowels': 550.0, 'first': 'p'}
{'total': 120.0, 'cons': 73.0, 'vowels': 47.0, 'first': 'j'}
{'total': 488.0, 'cons': 299.0, 'vowels': 189.0, 'first': 'l'}
{'total': 260.0, 'cons': 143.0, 'vowels': 117.0, 'first': 'v'}
{'total': 1112.0, 'cons': 630.0, 'vowels': 482.0, 'first': 'e'}
{'total': 988.0, 'cons': 574.0, 'vowels': 414.0, 'first': 'r'}
{'total': 60.0, 'cons': 32.0, 'vowels': 28.0, 'first': 'q'}
{'total': 4.0, 'cons': 2.0, 'vowels': 2.0, 'first': 'z'}
在Python应用程序中使用聚合来生成数据
在本节中,您将编写一个简单的Python应用程序,它使用Collection对象的方法aggregate()从示例数据库检索各种聚合数据。通过这个示例,您将熟悉如何使用aggregate()来利用聚合流水线在MongoDB服务器上处理数据,再返回结果。程序清单17.11显示了这个示例的代码。
在这个示例中,函数__main__连接到MongoDB数据库,获取一个Collection对象,并调用其他方法来聚合数据并显示结果。方法displayAggregate()显示聚合结果。
方法largeSmallVowels()使用了一条包含运算符$match、$group和$sort的聚合流水线,这条流水线查找以元音字母开头的单词,根据第一个字母将这些单词分组,并找出各组中最长和最短单词的长度。
方法top5AverageWordFirst()使用了一条包含运算符$group、$sort和$limit的聚合流水线,这条流水线根据第一个字母将单词分组,并找出单词平均长度最长的前5组。
请执行下面的步骤,创建并运行这个Python应用程序,它使用聚合流水线来处理示例数据集中的文档,并显示结果。
1.确保启动了MongoDB服务器。
2.确保下载并安装了Python MongoDB驱动程序,并运行了生成数据库words的脚本文件code/hour05/generate_words.js。
3.在文件夹code/hour17中新建一个文件,并将其命名为PythonAggregate.py。
4.在这个文件中输入程序清单17.11所示的代码。这些代码对文档集执行aggregate()操作。
5.将这个文件存盘。
6.打开一个控制台窗口,并切换到目录code/hour17。
7.执行下面的命令来运行这个Python应用程序。程序清单17.12显示了这个应用程序的输出。
程序清单17.11 PythonAggregate.py:在Python应用程序中使用聚合流水线生成数据集
程序清单17.12 PythonAggregate.py-output:在Python应用程序中使用聚合流水线生成数据集的输出
▲
在Python应用程序中使用MongoDB时,另一个很有用的工具是聚合框架。Collection对象提供了对数据执行聚合操作的方法aggregate(),这个方法的语法如下:
aggregate(operator, [operator, ...])
参数operator是一系列运算符对象,提供了用于聚合数据的流水线。这些运算符对象是使用聚合运算符创建的Dictionary对象。聚合运算符在第9章介绍过,您现在应该熟悉它们。
例如,下面的代码定义了运算符$group和$limit,其中运算符$group根据字段word进行分组(并将该字段的值存储在结果文档的_id字段中),使用$avg计算size字段的平均值(并将结果存储在average字段中)。请注意,在聚合运算中引用原始文档的字段时,必须在字段名前加上$:
group = {'$group' :
{'_id' : '$word',
'average' : {'$avg' : '$size'}}}
limit = {'$limit' : 10}
result = collection.aggregate([group, limit])
方法aggregate()返回一个Dictionary对象。这个Dictionary对象包含一个result键,而该键对应的值是一个包含聚合结果的列表。为演示这一点,下面的代码逐项显示聚合结果的内容:
for result in results['result']:
print (result)
▼ Try It Yourself
python PythonAggregate.py
01 from pymongo import MongoClient
02 def displayAggregate(results):
03 for result in results['result']:
04 print (result)
05 def largeSmallVowels(collection):
06 match = {'$match' :
07 {'first' :
08 {'$in' : ['a','e','i','o','u']}}}
09 group = {'$group' :
10 {'_id' : '$first',
11 'largest' : {'$max' : '$size'},
12 'smallest' : {'$min' : '$size'},
13 'total' : {'$sum' : 1}}};
14 sort = {'$sort' : {'first' : 1}};
15 result = collection.aggregate([match, group, sort])
16 print ("\nLargest and smallest word sizes for " + \
17 "words beginning with a vowel:")
18 displayAggregate(result)
19 def top5AverageWordFirst(collection):
20 group = {'$group' :
21 {'_id' : '$first',
22 'average' : {'$avg' : '$size'}}}
23 sort = {'$sort' : {'average' : -1}}
24 limit = {'$limit' : 5}
25 result = collection.aggregate([group, sort, limit]);
26 print ("\nFirst letter of top 5 largest average " + \
27 "word size:")
28 displayAggregate(result)
29 if __name__=="__main__":
30 mongo = MongoClient('mongodb://localhost:27017/')
31 db = mongo['words']
32 collection = db['word_stats']
33 largeSmallVowels(collection)
34 top5AverageWordFirst(collection)
Largest and smallest word sizes for words beginning with a vowel:
{'total': 150, '_id': 'e', 'smallest': 3.0, 'largest': 13.0}
{'total': 33, '_id': '', 'smallest': 2.0, 'largest': 13.0}
{'total': 114, '_id': 'i', 'smallest': 1.0, 'largest': 14.0}
{'total': 72, '_id': 'o', 'smallest': 2.0, 'largest': 12.0}
{'total': 192, '_id': 'a', 'smallest': 1.0, 'largest': 14.0}
First letter of top 5 largest average word size:
{'average': 7.947368421052632, '_id': 'i'}
{'average': 7.42, '_id': 'e'}
{'average': 7.292134831460674, '_id': 'c'}
{'average': 6.881818181818182, '_id': 'p'}
{'average': 6.767123287671233, '_id': 'r'}
在本章中,您学习了如何使用Collection和Cursor对象的其他方法。您了解到,方法limit()可减少游标返回的文档数,而结合使用方法limit()和skip()可分页显示大型数据集。使用方法find()的参数fields可减少从数据库返回的字段数。
本章还介绍了如何在Python应用程序中使用Collection对象的方法distinct()、group()和aggregate()来执行数据汇总操作。这些操作让您能够在服务器端处理数据,再将结果返回给Python应用程序,从而减少了需要发送的数据量以及应用程序的工作量。
答:可以。在Python MongoDB驱动程序中,Database对象包含方法comand(),这个方法将要在MongoDB服务器上执行的命令作为参数。
答:可以。Python MongoDB驱动程序提供bson.BSON类,这个类包含方法decode(dict)和encode(BSON),它们分别将BSON对象编码为数组以及将数组解码为BSON对象。
作业包含一组问题及其答案,旨在加深您对本章内容的理解。请尽可能先回答问题,再看答案。
1.在Python中,如何获取Cursor对象表示的第21~30个文档?
2.在Python应用程序中,如何找出集合中文档的特定字段的不同值?
3.在Python中,如何返回集合的前10个文档?
4.在Python中,如何禁止数据库查询返回特定的字段?
1.对Cursor对象调用limit(10)和skip(20)。
2.使用Collection对象的方法distinct()。
3.对Cursor对象调用limit(10)。
4.在传递给方法find()的参数fields中,将这个字段的值设置为false。
1.编写一个Python应用程序,找出示例数据集中以n打头的单词,根据长度降序排列它们,并显示前5个单词。
2.扩展文件PythonAggregate.py,在其中添加一个执行聚合的方法,它匹配长度为4的单词,将返回文档限制为5个,并返回字段word和stats(但将字段word重命名为_id)。这种聚合操作对应的MongoDB shell聚合流水线类似于下面这样:
{$match: {size:4}},
{$limit: 5},
{$project: {_id:"$word", stats:1}}
本章介绍如下内容:
复制、重命名和移动集合;
添加和删除索引;
对MongoDB数据库执行检查(validation);
为优化性能而评估查询;
找出并诊断有问题的集合和数据库;
备份MongoDB数据库;
修复MongoDB数据库。
在本书前面,大部分章节讨论的都是如何实现MongoDB数据库,包括从MongoDB shell和其他编程平台创建、填充、访问和操作集合。本章将视线转向使用MongoDB shell管理MongoDB数据库。
数据库管理涉及的工作很多,具体内容随情况而异。但一般而言,管理指的是采取所有必要的措施确保数据库健康、可用。
本章首先介绍一些基本的数据库操作,如复制、移动和重命名数据库和集合。然后,介绍如何创建和管理索引,帮助您找到优化数据库的途径。接下来,将探讨多种确保数据库健康的性能和诊断任务。最后,探讨如何修复和备份数据库。
第5章介绍了如何创建、访问和删除数据库和集合,本节扩展这方面的知识,介绍其他一些不那么常见但很有用的任务:复制数据库、重命名集合和创建固定集合。
复制MongoDB数据库
本节介绍一个数据库复制示例,演示使用命令copydb创建示例数据库拷贝的步骤。请执行如下步骤来创建示例数据库的拷贝。
1.确保启动了MongoDB服务器。
2.确保运行了生成数据库words的脚本文件code/hour05/generate_words.js。
3.使用下面的命令启动MongoDB shell:
4.使用下面的命令切换到数据库admin:
5.使用下面的命令创建数据库words的拷贝,并将其命名为words_copy:
6.使用下面的命令切换到数据库words_copy,并核实其中确实包含文档:
▲
在MongoDB中,可在服务器之间复制数据库,还可将数据库复制到当前服务器的其他位置。您可能需要复制数据库,这可能旨在将数据从要拆除的服务器中移走,也可能旨在提供多个用于不同目的的数据拷贝。
要在服务器之间复制数据库,需要在数据库admin中执行命令copydb。这个命令接受一个对象,其中包含如下参数。
fromhost:可选参数。指定源mongod实例的主机名,如果未指定,copydb将在当前MongoDB服务器内复制数据库。
fromdb:必须指定的参数。指定源数据库的名称。
todb:必须指定的参数。指定目标命名空间的名称。
slaveOk:可选的布尔参数。如果为true,copydb将从副本集的备份成员复制数据,也可从主成员复制数据。
username:可选参数。指定MongoDB服务器fromhost上的用户名凭证。
key:可选参数。指定向fromhost服务器验证身份时使用的密码的散列值。
▼ Try It Yourself
mongo
use admin
db.runCommand({ copydb: 1,
fromhost: "localhost",
fromdb: "words",
todb: "words_copy"})
use words_copy
db.word_stats.find().count()
重命名MongoDB数据库中的集合
本节介绍一个集合重命名示例,演示使用命令renameCollection将一个集合重命名,并存储到另一个数据库中的步骤。重命名集合时,可指定不同的集合名并将其保留在原来的数据库中,还可保留集合名并将其放在另一个数据库中。请执行如下步骤,将示例数据库拷贝中的集合重命名。
1.确保启动了MongoDB服务器。
2.确保运行了生成数据库words的脚本文件code/hour05/generate_words.js,并完成了前一小节的数据库复制示例。
3.使用下面的命令启动MongoDB shell:
4.使用下面的命令切换到数据库admin:
5.使用下面的命令将数据库words_copy的集合word_stats重命名为word_stats2,并放到数据库words_copy2中:
6.使用下面的命令切换到数据库words_copy2,并核实其中的集合word_stats2确实包含一些文档:
▲
对集合执行的另一种常见任务是重命名。通过将集合重命名,可将原来的名称用于存储新数据的集合。这在有些情况下很有用。例如,您可能有一个应用程序,它将订单存储在集合orders中,而您只想在这个集合中存储当月的订单。为此,可在每个月的月底将集合orders重命名为orders_MM_YYYY(其中MM为月份,而YYYY为年份),从而让应用程序能够使用集合orders来存储下一个月的订单。
要重命名集合,可在数据库admin中使用命令renameCollection。这个命令接受一个包含如下参数的对象。
renameCollection:必须指定的参数。指定要重命名的集合的命名空间,格式为database.collection。
todb:必须指定的参数。指定重命名后的命名空间,格式为database.collection。如果指定的数据库不存在,将创建它。
dropTarget:可选的布尔参数。如果为true,将删除同名的集合;否则保留原来的集合,而重命名将以失败告终。
▼ Try It Yourself
mongo
use admin
db.runCommand({ renameCollection: "words_copy.word_stats",
to: "words_copy2.word_stats2",
dropTarget: true})
use words_copy2
db.word_stats2.find().count()
在MongoDB数据库中创建固定集合
本节介绍使用命令renameCollection及其capped选项在MongoDB数据库中创建固定集合的步骤。请执行如下步骤来创建一个固定集合。
1.确保启动了MongoDB服务器。
2.使用下面的命令启动MongoDB shell:
3.使用下面的命令切换到数据库capped(这个数据库原本不存在,但通过执行下面的命令将创建它):
4.使用下面的命令创建固定集合myCapped,它最多可存储5个文档:
5.使用下面的代码插入10个文档,这些文档只包含值为0~9的字段num:
6.使用下面的命令显示集合myCapped中的文档:
7.输出表明这个集合中只有5个(而不是10个)文档,它们的num字段值为5~9:
▲
固定集合是大小固定的集合,检索和删除文档时都基于插入顺序,这让固定集合能够支持高吞吐量的操作。固定集合的工作原理类似于环形缓冲区:分配给固定集合的空间耗尽后,将覆盖最旧的文档,为新文档腾出空间。
定义固定集合时,还可指定它最多存储多少个文档,这可避免在集合中存储大量文档带来的索引开销。
固定集合非常适合用于存储事件日志和缓存数据,这可避免扩展集合的开销,还可避免在应用程序中编写清理集合的代码。
要在MongoDB shell中创建固定集合,可使用db对象的方法createCollection(),并将属性capped设置为true、设置集合的大小(单位为字节)以及可选的最大文档数,如下所示:
db.createCollection("log", { capped : true, size : 5242880, max : 5000 } )
mongo
▼ Try It Yourself
use capped
db.createCollection("myCapped", { capped : true, size : 1048576, max : 5 } )
for (i=0; i<10; i++){
db.myCapped.insert({num: i})
}
db.myCapped.find()
{ "_id" : ObjectId("52f2bbc01f621a31b41c50ba"), "num" : 5 }
{ "_id" : ObjectId("52f2bbc01f621a31b41c50bb"), "num" : 6 }
{ "_id" : ObjectId("52f2bbc01f621a31b41c50bc"), "num" : 7 }
{ "_id" : ObjectId("52f2bbc01f621a31b41c50bd"), "num" : 8 }
{ "_id" : ObjectId("52f2bbc01f621a31b41c50be"), "num" : 9 }
MongoDB数据库管理的另一个重要方面的实现索引。索引创建简单的查找表,让MongoDB能够更快地查找文档。
警告:
索引可改善数据库请求的性能,但这需要付出一定的代价。每次在集合中插入新文档时,都必须调整索引。如果数据库是写入密集型的,索引过多可能严重影响性能。*
接下来的几小节介绍一些与索引相关的管理任务,如添加索引、删除索引和重建索引。
在MongoDB中,可根据集合中的字段创建索引,以提高文档查找速度。在MongoDB中添加索引时,将在后台创建一个特殊的数据结构(其中存储了集合的一小部分数据),再对其进行优化以提高查找特定文档的速度。
例如,根据_id字段创建索引时,将创建一个包含_id值的有序数组。创建这种索引有下面这些好处。
按_id查找文档时,可在这个有序索引中搜索,从而更快地找到文档。
假设您希望返回的文档按_id排序,这种排序已在索引中完成,因此不需要再这样做,MongoDB只需按_id在索引中出现的顺序返回文档。
现在假设您希望文档按_id排序,并返回第10~20个文档。为此,只需截取索引中的这部分_id,再根据_id查找文档。
最重要的是,如果您要获取有序的_id值列表,MongoDB根本不需要读取文档,而只需直接返回索引中的值。
然而,别忘了获得这些好处是要付出代价的。下面是索引的一些开销。
索引需要占用磁盘和内存空间。
插入和更新文档时,索引将占用处理时间。这意味着集合包含大量索引时,将影响数据库写入操作的性能。
集合越大,索引在资源和性能方面的开销越高。在超大集合中,甚至都不适合创建索引。
在集合中,可根据设计需求创建多种索引。表22.1列出了各种索引类型。
表22.1 MongoDB支持的索引类型
类型 |
描述 |
---|---|
默认的_id索引 |
所有MongoDB集合默认都包含基于_id的索引。如果应用程序没有给文档指定_id值,MongoDB服务器将为文档创建包含ObjectID值的_id字段。_id索引是唯一的,禁止客户端插入两个_id值相同的文档 |
单字段索引 |
最简单的索引是单字段索引。这种索引类似于_id索引,但是根据指定的字段创建的。这种索引可按升序或降序排列,且不要求指定字段的值是唯一的。例如:{name: 1} |
复合索引 |
这种索引基于多个字段,它首先根据第一个字段排序,再根据第二个字段排序,依此类推。各个字段的排序方向可以不同,例如,可根据一个字段升序排列,并根据另一个字段降序排列,如{name: 1, value: -1} |
多键索引 |
基于数组字段创建索引时,将为数组中的每个元素创建一个索引项。这使得根据索引包含的值查找对象时速度更快。例如,如果有一个myObjs对象数组,其中每个对象都有score字段,将创建基于score字段的索引:{myObjs.score: 1} |
地理空间索引 |
MongoDB支持创建基于二维坐标或二维球面坐标的地理空间索引。这意味着您能更有效地存储和检索引用地理位置的数据。例如:{"locs":"2d"} |
全文索引 |
MongoDB还支持创建全文索引,这让使得根据单词查找字符串元素的速度更快。全文索引不会存储the、a和is等单词。例如:{comment: "text"} |
散列索引 |
使用基于散列的分片时,MongoDB支持创建散列索引,其中值包含存储在特定服务器中的散列值,这可避免在其他服务器中存储不相关散列值的开销。例如:{key: "hashed"} |
创建索引时,还可指定一些特殊属性(如表22.2所示),这些属性告诉MongoDB该如何处理索引。
表22.2 MongoDB支持的索引属性
属性 |
描述 |
---|---|
background |
布尔值。如果为true,MongoDB将在后台创建索引,这使得在索引创建期间允许对集合执行写入操作;如果为false,将在索引创建完毕前禁止对集合执行写入操作 |
unique |
禁止索引包含同一个字段值多次。因此,添加文档时,如果包含的字段值已出现在索引中,MongoDB将禁止添加该文档 |
sparse |
仅当文档包含索引字段时,索引中才会有其条目。这种索引忽略没有索引字段的文档 |
TTL |
存活时间(Time To Live,TTL)索引只让文档在索引中存在指定的时间。例如,这适用于特定时间后需要清除的日志条目和事件数据。这种索引记录插入时间,并在条目过期后将其删除 |
dropDups |
布尔值。如果为true且unique为true,将把索引字段与既有文档相同的所有文档删除 |
name |
可给索引指定名称,以便在其他命令中引用它 |
可结合使用属性unique和sparse,让索引拒绝索引字段为重复值的文档,并拒绝不包含索引字段的文档。
使用MongoDB shell和大部分MongoDB驱动程序都可创建索引。要使用MongoDB shell来创建索引,可使用方法ensureIndex(index, properties)。例如,下面的代码创建一个名为myIndex的唯一索引,该索引基于字段name(升序)和number(降序):
db.myCollection.ensureIndex({name:1, number: -1},
{background:true, unique:true , name: "myIndex"})
有时候需要将索引从集合中删除,因为它们占用的服务器资源太多或不再需要。删除索引很容易,只需使用Collection对象的方法dropIndex(index)即可。
方法dropIndex()将索引名或索引定义作为唯一的参数。例如,如果创建了索引{first:1},可这样将其删除:
db.myCollection.dropIndex({first:1})
另外,如果调用方法ensureIndex()创建索引时指定了索引名,则可根据索引名来删除。例如,如果有一个名为myIndex的索引,可这样将其删除:
db.myCollection.dropIndex("myIndex")
如果要删除集合的所有索引,可使用方法dropIndexes()。这将删除集合的所有索引,如下所示:
db.myCollection.dropIndexes()
管理MongoDB数据库中集合的索引
本节介绍如何给示例数据库的一个集合添加和删除索引。这里介绍的方法也适用于在其他数据库的集合中添加、显示和删除索引。请执行下面的步骤,在示例数据库的一个集合中添加和删除索引。
1.确保启动了MongoDB服务器。
2.确保运行了生成数据库words的脚本文件code/hour05/generate_words.js。
3.使用下面的命令启动MongoDB shell:
4.使用下面的命令切换到数据库words:
5.使用下面的命令显示数据库words中集合word_stats的索引,该命令后面是其输出:
6.使用下面的命令添加一个新索引,它基于字段first(升序)和size(降序):
7.使用下面的命令显示数据库words中集合word_stats的索引myIndex,该命令后面是其输出:
8.使用下面的命令删除这个新添加的索引:
9.使用下面的命令核实新索引myIndex已删除,该命令后面是其输出:
▲
与任何实现索引的数据库一样,您创建的索引也可能效果不佳,或者因受损而不再管用。在这种情况下,可使用Collection对象的方法reIndex()来重建索引。
方法reIndex()删除并重建集合的所有索引。
警告:
重建索引操作将占用大量系统资源,请务必在非高峰期间这样做。另外,_id索引的重建是在前台进行的,因此在该索引重建完毕前,不能执行写入操作。 *
▼ Try It Yourself
mongo
use words
db.word_stats.getIndexes()
[ { "v" : 1,
"key" : {"_id" : 1},
"ns" : "words.word_stats",
"name" : "_id_" },
{ "v" : 1,
"key" : {"word" : 1},
"unique" : true,
"ns" : "words.word_stats",
"name" : "word_1" } ]
db.word_stats.ensureIndex({first:1, size: -1},
{background:true, name: "myIndex"})
db.word_stats.getIndexes()
[ { "v" : 1,
"key" : {"_id" : 1},
"ns" : "words.word_stats",
"name" : "_id_"},
{ "v" : 1,
"key" : {"word" : 1},
"unique" : true,
"ns" : "words.word_stats",
"name" : "word_1"},
{ "v" : 1,
"key" : { "first" : 1, "size" : -1},
"ns" : "words.word_stats",
"name" : "myIndex",
"background" : true } ]
db.word_stats.dropIndex("myIndex")
db.word_stats.getIndexes()
[ { "v" : 1,
"key" : {"_id" : 1},
"ns" : "words.word_stats",
"name" : "_id_" },
{ "v" : 1,
"key" : {"word" : 1},
"unique" : true,
"ns" : "words.word_stats",
"name" : "word_1" } ]
数据库管理的一个重要方面是找出并诊断数据库的性能等问题。MongoDB提供了大量的功能,让您能够对查询、资源使用情况和其他信息进行分析,以评估数据库的健康状况,并找出导致数据库的性能等问题的罪魁祸首。
接下来的几小节介绍一些常见的工具,您可使用它们来找出悄然出现在MongoDB实现中的性能等问题。
检查MongoDB数据库的总体健康状况时,经常需要执行的一项任务是查看数据库的统计信息。数据库统计信息包括对象数、对象的平均大小、数据总量、索引的大小等,这些统计信息可帮助您判断数据库有多大,消耗了多少内存和磁盘资源。
要在MongoDB shell中查看数据库统计信息,可使用下面的方法:
db.stats()
这个方法的输出类似于下面这样:
{
"db" : "words",
"collections" : 3,
"objects" : 2679,
"avgObjSize" : 344.60768943635685,
"dataSize" : 923204,
"storageSize" : 2805760,
"numExtents" : 7,
"indexes" : 2,
"indexSize" : 204400,
"fileSize" : 50331648,
"nsSizeMB" : 16,
"dataFileVersion" : {
"major" : 4,
"minor" : 5
},
"ok" : 1
}
您还可以更进一步,查看集合的这些统计信息。这可帮助您确定集合占用的磁盘空间和索引空间。要查看集合的统计信息,可使用下面的方法,其中<collection>为集合的名称:
db.<collection>.stats()
检查MongoDB数据库
在本节中,您将对示例数据库进行检查。这个示例演示了如何执行检查并查看结果。
请执行下面的步骤来进行检查。
1.确保启动了MongoDB服务器。
2.确保运行了生成数据库words的脚本文件code/hour05/generate_words.js。
3.使用下面的命令启动MongoDB shell:
4.使用下面的命令切换到数据库words:
5.使用下面的命令执行检查:
6.查看检查结果。下面是方法validate()的输出示例,注意到属性valid为true,而属性errors为空,这表明这个集合没有问题:
▲
检查数据库健康状况时,另一个很有用的工具是Collection对象的方法validate()。这个方法通过扫描数据和索引来检查集合的结构,并在输出中报告发现的问题。
例如,要检查数据库words的集合word_stats,可像下面这样做:
use words
db.word_stats.validate()
您还可给方法validate()传入true,以执行更详细的检查。这将对数据进行更深入、更全面的扫描,但消耗的时间和服务器资源也更多,如下所示:
use words
db.word_stats.validate(true)
▼ Try It Yourself
mongo
use words
db.word_stats.validate()
{
"ns" : "words.word_stats",
"firstExtent" : "0:5000 ns:words.word_stats",
"lastExtent" : "0:109000 ns:words.word_stats",
"extentCount" : 5,
"datasize" : 922896,
"nrecords" : 2673,
"lastExtentSize" : 2097152,
"padding" : 1,
"firstExtentDetails" : {
"loc" : "0:5000",
"xnext" : "0:19000",
"xprev" : "null",
"nsdiag" : "words.word_stats",
"size" : 8192,
"firstRecord" : "0:50b0",
"lastRecord" : "0:6eb0"
},
"lastExtentDetails" : {
"loc" : "0:109000",
"xnext" : "null",
"xprev" : "0:41000",
"nsdiag" : "words.word_stats",
"size" : 2097152,
"firstRecord" : "0:1090b0",
"lastRecord" : "0:14af80"
},
"deletedCount" : 4,
"deletedSize" : 1826928,
"nIndexes" : 2,
"keysPerIndex" : {
"words.word_stats.$_id_" : 2673,
"words.word_stats.$word_1" : 2673
},
"valid" : true,
"errors" : [ ],
"ok" : 1
}
剖析MongoDB数据库
在本节中,您将对示例数据库进行剖析。这个示例演示了如何启用剖析、查看数据库操作的剖析文档以及禁用剖析。请执行下面的步骤来实现剖析。
1.确保启动了MongoDB服务器。
2.确保运行了生成数据库words的脚本文件code/hour05/generate_words.js。
3.使用下面的命令启动MongoDB shell:
4.使用下面的命令切换到数据库words:
5.使用下面的命令对耗时超过500毫秒的操作启用剖析:
6.执行下面的命令,它执行一个查询操作,以便在集合profile中填充操作剖析数据:
7.执行下面的命令来查看集合system.profile的内容:
8.查看输出,其中应包含一个类似于下面的文档,该文档为查询请求{word:"test"}的剖析文档:
9.执行下面的命令禁用剖析:
▲
如果数据库响应缓慢,可对其进行剖析(profile)。通过剖析,可捕获有关数据库性能的数据;随后您可查看这些数据,找出哪些查询的性能非常糟糕。
数据库剖析是一个很有用的工具,但也会影响性能,应仅在需要排除性能故障时启用它。
MongoDB提供了不同的剖析等级,这些等级用数字表示。下面描述了这些等级。
0:不剖析。
1:只剖析速度较慢的操作。
2:剖析所有操作。
要启用剖析,可使用数据库命令profile,并指定剖析等级以及缓慢操作的判断标准(耗时超过了多少毫秒)。
例如,下面的命令启用1级剖析,并将耗时超过500毫秒视为判断缓慢操作的标准:
mongo
db.runCommand({profile:1, slowms: 500})
剖析提供的信息存储在当前数据库的集合system.profile中,因此要访问剖析信息,可使用下面的方法:
use words
db.system.profile.find()
system.profile.find()返回的文档中包含请求的性能信息,表22.3描述了剖析文档的一些属性。
db.runCommand({profile:2, slowms: 500})
表22.3 MongoDB数据库操作剖析文档的属性
属性 |
描述 |
---|---|
op |
数据库操作类型,如插入、更新或查询 |
ns |
操作针对的命名空间,格式为database.collection |
query |
使用的查询文档 |
ntoreturn |
使用limit()返回的文档数 |
ntoskip |
使用skip()跳过的文档数 |
nscanned |
为执行操作扫描的文档数 |
lockStats |
一个文档信息,包含有关数据库锁的信息,如等了多长时间才获得锁 |
nreturned |
操作返回的文档数 |
responseLength |
响应的大小,单位为字节 |
millis |
执行完操作花了多少毫秒 |
ts |
一个ISO时间戳,指出了发出请求的时间 |
client |
发出请求的客户端的IP地址 |
user |
发出请求的用户——如果请求是通过经身份验证的连接发出的 |
db.word_stats.find({word: "test"})
另外,由于剖析数据存储在一个集合中,您可使用查询来指定要返回的字段。例如,下面的代码查看这样的操作的剖析数据,即耗时超过10秒且执行时间在指定的ISO时间之后:
db.system.profile.find(
{$and: [
{ts: {$gt: ISODate("2014-02-06T15:15:12.507Z")}},
{millis:{$lt:1}}]})
db.system.profile.find()
▼ Try It Yourself
{ "op" : "query", "ns" : "words.word_stats", "query" : { "word" : "test" },
<<toreturn" :0, "ntoskip" : 0, "nscanned" : 1, "keyUpdates" : 0,
<<umYield" : 0, "lockStats" :
{ "timeLockedMicros" : { "r" : NumberLong(510), "w" : NumberLong(0) },
<<imeAcquiringMicros": { "r" : NumberLong(9), "w" : NumberLong(4) } },
<<returned" : 1, "responseLength" :305, "millis" : 0,
<<s" : ISODate("2014-02-06T00:09:38.530Z"), "client" : "127.0.0.1",
<<llUsers" : [ ], "user" : "" }
db.runCommand({profile:0, slowms: 500})
分析MongoDB数据库查询
在本节中,您将使用方法explain()来查看MongoDB为针对示例数据库的查询使用的查询计划。这个示例演示了如何对Cursor对象调用方法explain(),并查看结果。
请执行如下步骤,对一个数据库查询运行explain()。
1.确保启动了MongoDB服务器。
2.确保运行了生成数据库words的脚本文件code/hour05/generate_words.js。
3.使用下面的命令启动MongoDB shell:
4.使用下面的命令切换到数据库words:
5.执行下面的语句,对一个针对示例数据库的简单查询运行explain():
6.查看输出;如果查询正常,输出应类似于下面这样。注意到在一个BtreeCursor游标中使用了索引word,而扫描的文档数只有5个,虽然集合包含的文档超过2000个:
▲
使用剖析找出耗时长的操作后,便可对其进行评估,看到MongoDB是如何执行查询的每个步骤的。评估查询通常可帮助您了解查询耗时长的原因。
要对查询进行评估,可对返回的游标调用方法explain(),也可以将这个方法串接到查询请求末尾,如db.collection.find({word: "test"}).explain()。
方法explain()返回一个文档,其中包含有关MongoDB服务器的查询计划的信息。查询计划描述了MongoDB如何找出与查询匹配的文档,包括索引使用情况。了解查询计划有助于优化查询。
表22.4列出了方法explain()返回的文档中的一些重要字段。
表22.4 explain()返回的文档的属性
属性 |
描述 |
---|---|
cursor |
使用的游标类型,可能取值如下。 BasicCursor:扫描整个集合 BtreeCursor:表明使用了索引 GeoSearchCursor:表明使用了地理空间索引 |
isMultiKey |
为true时表明查询使用了多键索引 |
n |
与查询参数匹配的文档数 |
nscanned |
操作期间扫描的文档数。通常,您希望n和nscanned尽可能接近,这意味着扫描的文档数是最少的 |
scanAndOrder |
如果为true,表明查询无法按索引顺序来返回排序结果 |
indexOnly |
如果为true,表明查询只需扫描索引,这意味着查询要返回的字段都包含在索引中。这种操作的速度通常是最快的 |
nYields |
为让写入操作能够完成,查询不得不让出读取锁的次数。这指出了繁忙的写入操作对数据库性能的影响程度 |
millis |
完成查询耗用的时间,单位为毫秒 |
indexBounds |
一个文档,包含遍历的索引范围的下限和上限 |
allPlans |
一个数组,包含查询优化器为查询选择索引而运行的查询计划列表 |
oldPlan |
一个文档,包含查询优化器为查询选择的前一个查询计划 |
clauses |
一个数组,包含$or操作的explain属性 |
numQueries |
执行的查询数 |
mongo
▼ Try It Yourself
use words
db.word_stats.find({word:{$in:['test','the','and']}}).explain()
{
"cursor" : "BtreeCursor word_1 multi",
"isMultiKey" : false,
"n" : 3,
"nscannedObjects" : 3,
"nscanned" : 5,
"nscannedObjectsAllPlans" : 3,
"nscannedAllPlans" : 5,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"word" : [
[
"and",
"and"
],
[
"test",
"test"
],
[
"the",
"the"
]
]
},
"server" : "bdub:27017"
}
分析MongoDB数据库的使用情况
在本节中,您将在MongoDB shell中执行top命令,以查看数据库的使用统计信息。这个示例演示了如何在MongoDB服务器上运行top命令并查看结果。
请执行如下步骤,在MongoDB服务器上运行top命令。
1.确保启动了MongoDB服务器。
2.确保运行了生成数据库words的脚本文件code/hour05/generate_words.js。
3.使用下面的命令启动MongoDB shell:
4.使用下面的命令切换到数据库admin:
5.执行下面的语句,对MongoDB服务器中的所有数据库运行top命令:
6.查看针对每个数据库中集合的输出,输出应类似于下面这样:
▲
为性能糟糕的数据库排除故障时,一个很有用的MongoDB命令是top。它返回每个数据库的使用统计信息,包含每种操作的执行次数以及花费的时间(单位为毫秒)。这有助于您确定哪些数据库使用的CPU时间最多以及哪些数据库最繁忙(这两类数据库通常不同)。
要执行top命令,可在数据库admin中执行如下命令:
use admin
db.runCommand({top: 1})
输出中包含在所有数据库的每个集合中以下操作类型的执行次数以及消耗的时间:
total
readLock
writeLock
queries
getmore
insert
update
remove
commands
▼ Try It Yourself
mongo
use admin
db.runCommand({top: 1})
"words.word_stats" : {
"total" : {
"time" : 107502,
"count" : 32
},
"readLock" : {
"time" : 77193,
"count" : 24
},
"writeLock" : {
"time" : 30309,
"count" : 8
},
"queries" : {
"time" : 43574,
"count" : 24
},
"getmore" : {
"time" : 6455,
"count" : 2
},
"insert" : {
"time" : 28968,
"count" : 1
},
"update" : {
"time" : 0,
"count" : 0
},
"remove" : {
"time" : 147,
"count" : 1
},
"commands" : {
"time" : 28358,
"count" : 4
}
},
想要修复MongoDB数据库的原因有多种。例如,系统可能崩溃、应用程序可能出现数据完整性问题、您可能想收回一些未用的磁盘空间。
要修复MongoDB数据库,可在MongoDB shell中进行,也可在mongod命令行中进行。要从命令行执行修复,可使用语法--repair和--repairpath <repair_path> syntax,其中<repair_path>为临时修复文件的存储位置,如下所示:
mongod --repair --repairpath /tmp/mongdb/data
要在MongoDB shell中执行修复,可使用命令db.repairDatabase(options),如下所示:
db.repareDatabase({ repairDatabase: 1,
preserveClonedFilesOnFailure: <boolean>,
backupOriginalFiles: <boolean> })
启动修复后,数据库中的所有集合都将被压缩,以减少占用的磁盘空间。另外,所有无效的记录都将被删除。因此,从备份恢复可能胜过运行修复。
运行修复所需的时间取决于数据量。修复会影响系统性能,应在非高峰期间运行。
警告:
如果副本集的其他成员有未受损的数据拷贝,就应使用该拷贝进行恢复,而不要试图去修复。repairDatabase()会将受损的数据删除,导致这些数据丢失。
对MongoDB而言,最佳的备份策略是使用副本集实现高可用性,这可确保数据是最新的且始终可用。然而,如果数据至关重要,无法承受其受损带来的损失,应考虑如下情况。
如果数据中心出现故障,该怎么办?对于这种情况,可定期备份数据并离线存储,或者添加离线的副本集。
如果应用程序数据受损并被复制,该怎么办?这始终是个令人担心的问题。对于这种情况,除了备份别无他法。
确定需要定期备份数据后,应考虑备份对系统的影响并制定相应的策略。
对生产环境的影响:备份通常是资源密集型的,必须尽可能降低其对生产环境的影响。
需求:如果打算采取类似于块级快照的方式备份数据库,需要确保系统基础设施支持这种方式。
分片:如果对数据进行了分片,所有分片都必须一致——不能备份一个分片,而不备份其他分片。另外,为生成实时备份,必须停止将数据写入集群。
相关数据:为降低备份对系统的影响,也可只备份对系统来说生死攸关的数据。例如,对于永远不会变的数据库,只需备份一次。如果数据库很容易重新生成但非常大,那么相比于频繁备份,可能值得付出重新生成的代价。
备份MongoDB数据库的主要方法有两种。一是使用命令mongodump进行二进制转储。您可将离线存储这些二进制数据,供以后使用。例如,要将主机mg1.test.net上的副本集和独立系统mg2.test.net的数据库,转储到文件夹/opt/backup/current,可使用下面的命令:
mongodump --host rset1/mg1.test.net:27018,mg2.test.net –out/ opt/backup/current
要恢复使用mongodump转储的数据库,可使用命令mongorestore。要使用mongorestore,最简单的方式是,在关闭了MongoDB服务器的情况下使用如下语法:
mongorestore --dbpath <database path> <path to the backup>
例如:
mongorestore --dbpath /opt/data/db /opt/backup/current
也可在运行着MongoDB服务器的情况下使用下面的语法来恢复:
mongorestore --port <database port> <path to the backup>
备份MongoDB数据库的第二种方法是使用文件系统快照。快照很容易拍摄,但也大得多,要求启用日记,并要求系统支持块级备份。如果您想实现快照备份方法,请参阅下述网址的指南:http://docs.mongodb.org/manual/tutorial/back-up-databases-with-filesystem-snapshots/。
在本章中,您探索了与确保数据库健康、可用相关的MongoDB数据库管理概念。首先,您学习了如何复制、重命名和移动集合;接下来,添加并删除了索引,并学习了如何为集合重建索引。
您还探索了MongoDB提供的多个性能和诊断工具。命令validate让您能够检查数据库,找出所有显而易见的问题;使用剖析可确定哪种操作花费的时间最多;您学习了如何对查询进行评估,以了解MongoDB是如何使用索引的;然后,您学习了如何使用命令top来收集集合的使用统计信息。
本章的最后两节介绍了如何修复MongoDB数据库,阐述了一些MongoDB数据库备份方法。
答:在这种情况下,使用该索引的每个请求都从磁盘读取索引,导致系统性能急剧下降。应考虑将这种索引分解成更小的索引。
答:有。MongoDB管理服务(MongoDB Management Services,MMS)是一种用于监控MongoDB部署的免费服务,更详细的信息请参阅https://mms.mongodb.com/。
作业包含一组问题及其答案,旨在加深您对本章内容的理解。请尽可能先回答问题,再看答案。
1.如何查看集合的使用统计信息,包括更新操作次数和花费的时间?
2.怀疑MongoDB数据库有问题时,如何修复它?
3.如何移动集合?
4.如何给集合添加基于字段name和number的唯一索引?
1.使用命令db.runCommand({top: 1})。
2.对数据库调用方法db.repairDatabase()(最好在非高峰期间这样做)。
3.使用命令renameCollection并将dropTarget设置为true。
4.对集合调用方法ensureIndex({name:1, number:1}, {unique: true})。
1.在MongoDB shell中使用下面的命令修复示例数据库:
use words
db.repareDatabase({ repairDatabase: 1,
preserveClonedFilesOnFailure: false,
backupOriginalFiles: false })
2.使用命令mongodbdump备份您的MongoDB服务器中的数据库;检查备份位置,核实成功地完成了备份;使用mongodbrestore将数据库恢复到文件夹<code>的如下位置:
<code>/hour22/restore
接下来,使用下面的命令启动mongod并将数据目录指定为restore,再核实其中包含数据库words。
mongod --dbpath <code>/hour22/restore