8. 性能与工具

最后一章,我们将介绍一些性能相关的话题,以及MongoDB开发者可以使用的工具。我们不会过深地涉及这些话题,但当中重要的部分都会有所介绍。

索引

最开始的时候我们介绍了特殊的system.indexes集合,它含有数据库中所有索引的信息。MongoDB中的索引和关系数据库很像:都有助于改进查询和排序的性能。索引是通过ensureIndex创建的:

// "name"是域的名字
db.unicorns.ensureIndex({name: 1});

并由dropIndex丢弃:

db.unicorns.dropIndex({name: 1});

要创建唯一的索引可以将第二个参数unique设为true

db.unicorns.ensureIndex({name: 1}, {unique: true});

索引可以在嵌入域(使用.符号)和数组域上。也可以创建复合索引:

db.unicorns.ensureIndex({name: 1, vampires: -1});

索引的顺序(1为升序,-1为降序)对于单键索引没有关系,不过对于复合索引来说在排序或是使用范围条件时就有影响了。

索引页有更多关于索引的信息。

Explain

要知道索引有没有使用索引,可以对游标使用explain方法:

db.unicorns.find().explain()

命令的结果告诉我们查询使用的是BasicCursor(也就是说没有用索引),扫描了12个对象,用了多长时间, 是否用了什么索引,如果有用索引的话还有一些额外有用的信息。

如果我们改用索引来查询,我们会看到的查询使用的BtreeCursor,以及用以查询的索引:

db.unicorns.find({name: 'Pilot'}).explain()

射后不理的写操作

之前我们有提到过,MongoDB中的写操作默认为射后不理。这样做可以获得一定的性能提高,同时也带来了系统崩溃时丢失数据的风险。有意思的是这种类型的写操作在插入/更新破坏了某唯一的约束时,是不返回错误的。若需要得到写失败的通知,就要在插入后调用db.getLastError()。很多驱动都把这一细节封装起来了,取而代之的是安全的写 操作——往往会多提供一个参数用来设置。

可惜的是shell并不提供安全的插入,因此我们就无法实验这一特性了。

分片(sharding)

MongoDB支持自动分片。分片技术是将数据水平切分存储在多台服务器上以实现可扩展性的一种方法。比较简单的实现可以将所有以A至M字母开头的用户信息存在1号服务器上然后剩下的都存在2号服务器上。幸运的是,MongoDB的分片功能远远超过了这种简单的方法。不过分片已经超出了本书要讨论的范畴,但您应该知道它的存在并且在系统需要用到多台服务器时会考虑这种技术。

复制

MongoDB的复制于关系数据库的复制类似。写入的数据发送到主服务器,主服务器再与其他从服务器进行同步。读操作可以选择在从服务器上做或者是在主服务器上做。当主服务器宕机的时候,可以将一台从服务器升级为新的主服务器。这样做可以分散系统的负荷,不过有读到陈旧数据的可能。MongoDB的复制也超出了本书要讨论的范围。

虽然复制可以提高性能(通过分散读操作),它的主要作用还是增加可靠性。将分片和复制结合是一种很普遍的做法。例如,每一个分片都可以由一个主服务器和一个从服务器维护。(从技术角度上还需要一个仲裁机以解决两个从服务器试图升级为主服务器的问题。不过仲裁机耗费的资源非常少,因此可以用在多个分片上)

统计

可以通过db.stats()获得数据库的数据统计信息。当中的大多数都和数据库的大小有关。也可以获取某个集合的统计信息。比如说可以用db.unicorns.stats()获得unicorns的相关信息。这些信息大部分还是和集合的大小相关。

网络接口

在MongoDB启动时的信息中有一个基于网络的管理工具的链接(如果您在shell中向上翻页到启动mongod时的部分应该还可以看到)。可以在浏览器中输入http://localhost:28017/以访问该工具。为了更好的使用这个工具,还需要在配置文件中加入rest=true并重启mongod进程。网络接口提供了很多关于服务器当前状态的信息。

分析器(Profiler)

下面的命令将启动MongoDB的分析器:

db.setProfilingLevel(2);

启动之后,可以运行下面的命令:

db.unicorns.find({weight: {$gt: 600}});

再读取分析器中的值:

db.system.profile.find()

最后的输出提供了这些信息:什么时候运行了什么命令,有多少文档被扫描过以及返回了多少数据。

可以将参数设成0,用setProfileLevel再次关闭分析器。当把参数设为1时,只有对耗时多于100毫秒的查询才会进行分析。或者也可以在第二个参数中指明最少的时间,以毫秒为单位:

//分析所有耗费时间多于一秒的操作
db.setProfilingLevel(1, 1000);

备份和恢复

在MongoDB的bin目录中有一个mongodump可执行文件。执行这一文件将连接到localhost并且将所有的数据库备份到一个叫做dump的子目录中。更多的选项可以通过mongodump --help获得。常见的选项有--db DBNAME,用以对某一特定的数据库进行备份,还有--collection COLLECTIONAME可以用来备份一个集合。利用bin下的mongorestore可以恢复上一次的备份。同样的,用--db--collection参数可以恢复某一指定的数据库或者集合。

例如,以下命令将把我们创建的learn数据库备份到backup目录(该命令是在终端窗口下输入的,而非mongo的shell中):

mongodump --db learn --out backup

如果只需要恢复unicorns集合,可以这样做:

mongorestore --collection unicorns backup/learn/unicorns.bson

需要指出的是mongoexportmongoimport这两个可执行文件可以用以输出和导入JSON或是CSV中的数据。例如可以输出为JSON格式的数据:

mongoexport --db learn -collection unicorns

或者是CSV格式的输出:

mongoexport --db learn -collection unicorns --csv -fields name,weight,vampires

请注意,mongoexportmongoimport并不总是能反应真实的数据。只有mongodumpmongorestore可以用以真正的备份。

本章小结

在这章中我们看到了MongoDB的不同命令、工具以及性能方面的细节。并不是所有的东西都有介绍,我们只选了最常见的那些。MongoDB的索引和关系数据库中的索引很相像,其他的很多工具也是这样。但是,很多工具的使用在MongoDB中更简洁扼要。