MongoDB中的常见问题(一)——基础和索引

00:00/00:00

本周内容主要翻译的是MongoDB官方文档中的常见问题,适用于刚使用MongoDB的新手。

MongoDB基础

本部分主要回答关于MongoDB的一些常见问题。

MongoDB支持什么平台?

支持的平台列表如下:

  • x86_64
  • ARM64
  • PPC64LE(MongoDB企业版)
  • s390x(MongoDB企业版)
    推荐的平台:
  • Amazon Linux
  • Debian 7.1
  • RHEL / CentOS 6.2+
  • SLES 11+
  • Ubuntu LTS 12.04
  • Ubuntu LTS 14.04
  • Windows Server 2012 & 2012 R2

MongoDB是否支持主机服务?

是的,MongoDB Altas是一个基于云主机的数据库即服务。可以访问文档了解更多。

集合与表有何区别?

与表不同,MongoDB数据库将数据以集合的形式进行存储。一个集合中包含一个或多个BSON文档。文档类似于一个关系型数据库表格中的记录或行。每个文档有一个或者多个字段,字段与关系型数据库表中的列类似。

我们如何创建一个数据库和集合?

如果数据库不存在,MongoDB会在我们第一次向该数据库存储数据时创建该数据库。

如果一个集合不存在,MongoDB会在我们第一次向该集合存储数据时创建该集合。

因此,我们可以切换到一个不存在的数据库(use <dbname>),然后执行下面的操作:

use myNewDB
db.myNewCollection1.insertOne( { x : 1 } )
db.myNewCollection2.createIndex( { a : 1 } )

如果数据库和集合之前都不存在的话,insert操作将会创建myNewDB数据库和myNewCollection1集合。

如果 myNewCollection2集合不存在,出现在myNewDB创建之后的createIndex操作,将会创建该集合和相应的索引。如果myNewDB也不存在,那么createIndex操作也将会创建该数据库。

如果我们想指定特定选项(例如,集合的大小限制以及文档验证规则等),也可以使用db.createCollection来显示地创建集合。

我们如何定义或者调整集合模式?

我们不需要指定MongoDB集合中集合的模式。尽管一般说来,集合中的文档拥有大部分同构结构,但是这并不是强制需求,例如,一个集合中的文档不需要有相同的字段集合。同一个集合中的文档之间某个字段的数据类型也可以不同。

如果需要修改集合中文档的结构,那么只需要将文档更新到新结构。例如,增加新字段、删除已有字段或者更新某个字段的值到一个新类型。

从3.2版本开始:我们可以设置集合在更新和插入操作过程中需要满足的文档验证规则。

一些集合属性,例如集合大小,可以在集合的显示创建过程中指定和修改。如果我们不需要指定这些属性,我们不需要显示创建这些集合,因为MongoDB在我们第一次向该集合存储数据时会创建新的集合。

MongoDB是否支持SQL?

不能,但是MongoDB支持自身的富查询语言。

MongoDB是否支持事务?

MongoDB不支持多文档事务。然而,MongoDB确实提供了单一文档中的原子操作。

MongoDB是否处理缓存?

是的,MongoDB会在内存中维护最近使用的大部分数据。如果我们对查询创建了索引,并且我们的工作数据集能够全部放在内存中,MongoDB将会直接在内存中处理所有查询。

MongoDB不会对查询结果进行缓存,为了针对相同的查询返回缓存结果。(?感觉文档这个部分有点问题,MongoDB是不会对结果进行缓存的,因为每次查询到的应该都是数据库中最新的数据。)

MongoDB如何处理SQL或者查询映射?

BSON
客户端向MongoDB发送一个请求时,它会创建一个BSON对象,而不是字符串。因此,并不会出现传统的SQL注入攻击。

MongoDB将查询标识为BSON对象,一般说来,客户端库都会提供一个翻遍的映射进程来创建这些对象。考虑下面的C++案例:

BSONObj my_query = BSON( "name" << a_name );
auto_ptr<DBClientCursor> cursor = c.query("tutorial.persons", my_query);

在这里,my_query将会有一个类似于{"name" : Joe}的值。如果my_query中包含特殊的字符,例如,,:{,该查询将不会匹配任何文档。例如,用户不能篡改查询并且将它删除。

JavaScript
通过在命令行传递--noscripting或者在配置文件中设置security.javascriptEnabled,我们可以禁止所有服务器端JavaScript的执行。

下面所有MongoDB操作允许我们直接在服务器上直接运行随意的JavaScript表达式:

  • $where
  • mapReduce
  • group

在这些情况下,我们必须特别注意,防止用户提交恶意的JavaScript。

幸运的是,我们可以在MongoDB中不使用JavaScript表示大部分查询,而对于那些需要JavaScript的查询,我们可以在一个单一查询中混合JavaScript和非JavaScript。将所有用户日工的数据直接放在BSON字段中,然后传递JavaScript代码到$where字段中。

如果我们需要在一个$where从句中传递用户提供的值,我们可以通过使用CodeWScope机制跳过这些值。当我们在域文档中将用户提交的值作为变量时没我们就可以避免在数据库服务器上对它们进行验证。

索引

本部分主要介绍索引相关的常见问题。

我们如何创建一个索引?

使用db.collection.createIndex()发方法在集合上创建索引。创建索引时一个管理性操作。一般说来,应用端不应该调用db.collection.ceateIndex()
注意
索引创建有可能会影响性能。管理员应该在构建索引之前考虑性能的影响。

索引创建如何影响数据库性能?

在集合上创建索引时,存储该集合的数据库都不能进行读写操作,直到索引创建结束。如果我们需要创建一个大型索引,考虑在后台创建索引。

可以使用下面的操作获得当前正在运行的索引创建操作的信息:

db.currentOp(
   {
     "active" : true,
     "numYields" : 0,
     "waitingForLock" : false
   }
)

如果需要停止一个运行的索引创建操作,可以使用db.killOp()。创建的部分索引将会被删除。

如何查看一个集合上的所有索引?

使用db.collection.getIndexes()方法来获取集合索引的列表。

我们如何决定索引哪些字段?

许多因素决定了在哪些字段上创建索引,包括:选择性、对多种查询形状的支持以及索引的大小等。

我们如何查看索引的大小?

db.collection.stats()包括一个索引大小文档,提供了集合上每个索引的大小信息。

根据索引大小的不同,一个索引可能不能完全放在内存中。当我们的服务器有足够的内存用于索引和剩下的工作机时,索引可以全部放在内存中。当一个索引大到无法放到内存中时,MongoDB必须从磁盘中读取索引,这样会花费更多的时间。

写操作如何影响索引?

写操作也许需要更新索引:

  • 如果一个写操作修改一个索引字段,MongoDB更新该修改字段的所有索引作为键。
  • 在MMAPv1存储引擎上运行时,如果对一个文档的会更新导致该文档的大小超过了给它分配的大小,MongoDB将会将文档移动到一个新的记录并且更新所有指向该文档的索引,而不用考虑修改的字段。

因此,如果我们的应用是写入特别多的应用,索引可能会影响性能。

打赏

mickey

记录生活,写给几十年后的自己。