在掌握了mongo的体系结构和基本操作后,开始学习 mongodb的优化,由于资源有限,只能网络上整理一些资料,我大致理解的mongo的优化分为以下几步:

 1.监控

mongodb可以通过profile来监控数据,进行优化(这些命令是在每个shard上执行查看的):

查看当前是否开启profile功能用命令:db.getProfilingLevel()     值为0|1|2,分别代表意思:0代表关闭,1代表记录慢命令,2代表全部。

level为1的时候,慢命令默认值为100ms,更改为db.setProfilingLevel(level,slowms)如db.setProfilingLevel(1,50)这样就更改为50毫秒。

通过db.system.profile.find() 查看当前的监控日志:db.system.profile.find({millis:{$gt:500}})   能够返回查询时间在500毫秒以上的查询命令

这里值的含义是

ts:命令执行时间                                                  如果发现时间比较长,那么就需要作优化。
info:命令的内容
query:代表查询
order.order: 代表查询的库与集合
reslen:返回的结果集大小,byte数                     reslen很大,有可能返回没必要的字段。
nscanned:扫描记录数量                                    比如nscanned数很大,或者接近记录总数,那么可能没有用到索引查询。
nquery:后面是查询条件
nreturned:返回记录数及用时                             nreturned很大,那么有可能查询的时候没有加限制。
millis:所花时间

mongo可以通过db.serverStatus()查看mongod的运行状态

2.索引

如果发现查询时间相对长,那么就需要做优化。首选就是为待查询的字段建立索引,不过需要特别注意的是,索引不是万能灵药。如果需要查询超过一半的集合数据,索引还不如直接遍历来的好。

索引的原理是通过建立指定字段的B树,通过搜索B树来查找对应document的地址。这也就解释了如果需要查询超过一半的集合数据,直接遍历省去了搜索B树的过程,效率反而会高。

关于索引,索引列颗粒越小越好,什么叫颗粒越小越好?在索引列中每个数据的重复数量称为颗粒,也叫作索引的基数。如果数据的颗粒过大,索引就无法发挥该有的性能。例如,我们拥有一个"age"列索引,如果在"age"列中,20岁占了50%,如果现在要查询一个20岁,名叫"Tom"的人,我们则需要在表的50%的数据中查询,索引的作用大大降低。所以,我们在建立索引时要尽量将数据颗粒小的列放在索引左侧,以保证索引发挥最大的作用。

3.explain 解析执行情况

 
> db.order.find({ "status": 1.0, "user.uid": { $gt: 2663199.0 } }).explain() { "cursor" : "BasicCursor",#游标类型 "nscanned" : 2010000,#扫描数量 "nscannedObjects" : 2010000,#扫描对象 "n" : 337800,#返回数据 "millis" : 2838,#耗时 "nYields" : 0, "nChunkSkips" : 0, "isMultiKey" : false, "indexOnly" : false, "indexBounds" : {#使用索引(这里没有) } }

作者:1angxi
链接:https://www.jianshu.com/p/b77a33fbe824
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

> db.order.find({ "status": 1.0, "user.uid": { $gt: 2663199.0 } }).explain() { "cursor" : "BasicCursor",#游标类型 "nscanned" : 2010000,#扫描数量 "nscannedObjects" : 2010000,#扫描对象 "n" : 337800,#返回数据 "millis" : 2838,#耗时 "nYields" : 0, "nChunkSkips" : 0, "isMultiKey" : false, "indexOnly" : false, "indexBounds" : {#使用索引(这里没有) } }

作者:1angxi
链接:https://www.jianshu.com/p/b77a33fbe824
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

> db.order.find({ "status": 1.0, "user.uid": { $gt: 2663199.0 } }).explain() { "cursor" : "BasicCursor",#游标类型 "nscanned" : 2010000,#扫描数量 "nscannedObjects" : 2010000,#扫描对象 "n" : 337800,#返回数据 "millis" : 2838,#耗时 "nYields" : 0, "nChunkSkips" : 0, "isMultiKey" : false, "indexOnly" : false, "indexBounds" : {#使用索引(这里没有) } }

作者:1angxi
链接:https://www.jianshu.com/p/b77a33fbe824
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

> db.order.find({ "status": 1.0, "user.uid": { $gt: 2663199.0 } }).explain() { "cursor" : "BasicCursor",#游标类型 "nscanned" : 2010000,#扫描数量 "nscannedObjects" : 2010000,#扫描对象 "n" : 337800,#返回数据 "millis" : 2838,#耗时 "nYields" : 0, "nChunkSkips" : 0, "isMultiKey" : false, "indexOnly" : false, "indexBounds" : {#使用索引(这里没有) } }

作者:1angxi
链接:https://www.jianshu.com/p/b77a33fbe824
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

shard02:PRIMARY> db.leochen_auth_log.find({"addtime":{$gt:"1524024720000"}}).explain()
{
        "cursor" : "BtreeCursor addtime_1",
        "isMultiKey" : false,
        "n" : 258,
        "nscannedObjects" : 258,
        "nscanned" : 258,
        "nscannedObjectsAllPlans" : 258,
        "nscannedAllPlans" : 258,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 2,
        "nChunkSkips" : 0,
        "millis" : 594,
        "indexBounds" : {
                "addtime" : [
                        [
                                "1524024720000",
                                {

}
                        ]
                ]
        },
        "server" : "shard02-pri.02.mongo.prod.uc:27037"
}

> db.order.find({ "status": 1.0, "user.uid": { $gt: 2663199.0 } }).explain() { "cursor" : "BasicCursor",#游标类型 "nscanned" : 2010000,#扫描数量 "nscannedObjects" : 2010000,#扫描对象 "n" : 337800,#返回数据 "millis" : 2838,#耗时 "nYields" : 0, "nChunkSkips" : 0, "isMultiKey" : false, "indexOnly" : false, "indexBounds" : {#使用索引(这里没有) } }

作者:1angxi
链接:https://www.jianshu.com/p/b77a33fbe824
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

4、数据库设计优化

在项目设计阶段,明确集合的用途是对性能调优非常重要的一步。

从性能优化的角度来看,集合的设计我们需要考虑的是集合中数据的常用操作,例如我们需要设计一个日志(log)集合,日志的查看频率不高,但写入频率却很高,那么我们就可以得到这个集合中常用的操作是更新(增删改)。如果我们要保存的是城市列表呢?显而易见,这个集合是一个查看频率很高,但写入频率很低的集合,那么常用的操作就是查询。

对于频繁更新和频繁查询的集合,我们最需要关注的重点是他们的范式化程度,假设现在我们需要存储一篇图书及其作者,在MongoDB中的关联就可以体现为以下几种形式:

1.完全分离(范式化设计)

示例1:


{
"_id" : ObjectId("5124b5d86041c7dca81917"),
"title" : "如何使用MongoDB",
"author" : [
ObjectId("144b5d83041c7dca84416"),
ObjectId("144b5d83041c7dca84418"),
ObjectId("144b5d83041c7dca84420"),
]
}

我们将作者(comment) 的id数组作为一个字段添加到了图书中去。这样的设计方式是在非关系型数据库中常用的,也就是我们所说的范式化设计。在MongoDB中我们将与主键没有直接关系的图书单独提取到另一个集合,用存储主键的方式进行关联查询。当我们要查询文章和评论时需要先查询到所需的文章,再从文章中获取评论id,最后用获得的完整的文章及其评论。在这种情况下查询性能显然是不理想的。但当某位作者的信息需要修改时,范式化的维护优势就凸显出来了,我们无需考虑此作者关联的图书,直接进行修改此作者的字段即可。

2.完全内嵌(反范式化设计)

示例2:


{
"_id" : ObjectId("5124b5d86041c7dca81917"),
"title" : "如何使用MongoDB",
"author" : [
{
     "name" : "丁磊"
     "age" : 40,
    "nationality" : "china",
},
{
     "name" : "马云"
     "age" : 49,
     "nationality" : "china",
},
{
     "name" : "张召忠"
     "age" : 59,
     "nationality" : "china",
},
]
}

在这个示例中我们将作者的字段完全嵌入到了图书中去,在查询的时候直接查询图书即可获得所对应作者的全部信息,但因一个作者可能有多本著作,当修改某位作者的信息时时,我们需要遍历所有图书以找到该作者,将其修改。

3.部分内嵌(折中方案)

示例3:


{
"_id" : ObjectId("5124b5d86041c7dca81917"),
"title" : "如何使用MongoDB",
"author" : [
{
    "_id" : ObjectId("144b5d83041c7dca84416"),
     "name" : "丁磊"
},
{
     "_id" : ObjectId("144b5d83041c7dca84418"),
     "name" : "马云"
},
{
     "_id" : ObjectId("144b5d83041c7dca84420"),
     "name" : "张召忠"
},
]
}

这次我们将作者字段中的最常用的一部分提取出来。当我们只需要获得图书和作者名时,无需再次进入作者集合进行查询,仅在图书集合查询即可获得。

这种方式是一种相对折中的方式,既保证了查询效率,也保证的更新效率。但这样的方式显然要比前两种较难以掌握,难点在于需要与实际业务进行结合来寻找合适的提取字段。如同示例3所述,名字显然不是一个经常修改的字段,这样的字段如果提取出来是没问题的,但如果提取出来的字段是一个经常修改的字段(比如age)的话,我们依旧在更新这个字段时需要大范围的寻找并依此进行更新。

在上面三个示例中,第一个示例的更新效率是最高的,但查询效率是最低的,而第二个示例的查询效率最高,但更新效率最低。所以在实际的工作中我们需要根据自己实际的需要来设计表中的字段,以获得最高的效率。

5、其他方法

热数据法

可能你的数据集非常大,但是这并不那么重要,重要的是你的热数据集有多大,你经常访问的数据有多大(包括经常访问的数据和所有索引数据)。使用MongoDB,你最好保证你的热数据在你机器的内存大小之下,保证内存能容纳所有热数据。

文件系统法

MongoDB的数据文件是采用的预分配模式,并且在Replication里面,Master和Replica Sets的非Arbiter节点都是会预先创建足够的空文件用以存储操作日志。这些文件分配操作在一些文件系统上可能会非常慢,导致进程被Block。所以我们应该选择那些空间分配快速的文件系统。这里的结论是尽量不要用ext3,用ext4或者xfs。

硬件法

这里的选择包括了对磁盘RAID的选择,也包括了磁盘与SSD的对比选择。

其他

如果数据文件大于系统内存,查询速度会下降几个数量级,因为mongodb是内存数据库。我以前测试过,1000万数据的时候没有索引情况下查询可能会几秒钟甚至更久。

这种情况,你最好给经常查询的项创建索引,有索引以后查询速度会非常非常非常的快。

另外一点是数据索引如果大于内存,速度也会下降很多。而且对于多条件查询,如果你查询的顺学和索引顺序不同,也不能使用索引。这个要慢慢摸索

如果你使用了replica set,这个会影响写入速度的,三个replica set,速度会降低到三分之一。

mongodb优化篇的更多相关文章

  1. MongoDB性能篇之创建索引,组合索引,唯一索引,删除索引和explain执行计划

    这篇文章主要介绍了MongoDB性能篇之创建索引,组合索引,唯一索引,删除索引和explain执行计划的相关资料,需要的朋友可以参考下 一.索引 MongoDB 提供了多样性的索引支持,索引信息被保存 ...

  2. Netty实现高性能RPC服务器优化篇之消息序列化

    在本人写的前一篇文章中,谈及有关如何利用Netty开发实现,高性能RPC服务器的一些设计思路.设计原理,以及具体的实现方案(具体参见:谈谈如何使用Netty开发实现高性能的RPC服务器).在文章的最后 ...

  3. 【转】Android性能优化之布局优化篇

     转自:http://blog.csdn.net/feiduclear_up/article/details/46670433 Android性能优化之布局优化篇 分类: andorid 开发2015 ...

  4. jQuery性能优化篇

    jQuery高级技巧——性能优化篇 阅读目录 通过CDN(Content Delivery Network)引入jQuery库 减少DOM操作 适当使用原生JS 选择器优化 缓存jQuery对象 定义 ...

  5. js 正则学习小记之匹配字符串优化篇

    原文:js 正则学习小记之匹配字符串优化篇 昨天在<js 正则学习小记之匹配字符串>谈到 个字符,除了第一个 个,只有 个转义( 个字符),所以 次,只有 次成功.这 次匹配失败,需要回溯 ...

  6. Spring+SpringMVC+MyBatis+easyUI整合优化篇(一)System.out.print与Log

    日常啰嗦 距离上一次更新博客有一段时间了,主要是因为最近有开发任务,另外,这段时间也在学习docker的相关知识,所以博客就没有继续写了,推荐一本书<Docker技术入门与实战>(第二版) ...

  7. Spring+SpringMVC+MyBatis+easyUI整合优化篇(二)Log4j讲解与整合

    日常啰嗦 上一篇文章主要讲述了一下syso和Log间的一些区别与比较,重点是在项目的日志功能上,因此,承接前文<Spring+SpringMVC+MyBatis+easyUI整合优化篇(一)Sy ...

  8. Spring+SpringMVC+MyBatis+easyUI整合优化篇(四)单元测试实例

    日常啰嗦 前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合优化篇(三)代码测试>讲了不为和不能两个状态,针对不为,只能自己调整心态了,而对于不能,本文会结合一 ...

  9. Spring+SpringMVC+MyBatis+easyUI整合优化篇(五)结合MockMvc进行服务端的单元测试

    日常啰嗦 承接前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合优化篇(四)单元测试实例>,已经讲解了dao层和service层的单元测试,还有控制器这层也不能 ...

随机推荐

  1. python eval 用法

    eval 功能:将字符串str当成有效的表达式来求值并返回计算结果. 语法: eval(source[, globals[, locals]]) -> value 参数: source:一个Py ...

  2. 笔记 oracle 创建主键自增长

    笔记 (1) 创建表 create table test( id number(18,2) primary key, -- 主键(unique+not null) name varchar2(100) ...

  3. IOS使用mkdir创建目录

    在IOS真机上可以创建目录的位置只有两个Documents和Caches,如果直接在NSHomeDirectory()上创建目录,会失败,返回的errno含义为操作被禁止. 获取Caches中的一个目 ...

  4. expect嵌套shell循环

    #!/bin/bash Detailtxt="test.txt" while read line do dest=`echo $line|awk '{print $1}'` ip= ...

  5. Bzoj4763 雪辉

    Time Limit: 39 Sec  Memory Limit: 666 MBSubmit: 151  Solved: 80 Description 上次立下的NOIP退役Flag没有成功   这次 ...

  6. Linux 下子进程与父进程的关系

    我们知道,Linux下父进程可以使用fork 函数创建子进程,但是当父进程先退出后,子进程会不会也退出呢? 通过下面这个小实验,我们能够很好的看出来: /******** basic.c ****** ...

  7. HDU 3065 病毒侵袭持续中 (AC自动机)

    题目链接 Problem Description 小t非常感谢大家帮忙解决了他的上一个问题.然而病毒侵袭持续中.在小t的不懈努力下,他发现了网路中的"万恶之源".这是一个庞大的病毒 ...

  8. 【译】第七篇 SQL Server代理作业活动监视器

    本篇文章是SQL Server代理系列的第七篇,详细内容请参考原文 在这一系列的上一篇,你创建并配置SQL Server代理作业.每个作业有一个或多个步骤,可能包含大量的工作流.在这篇文章中,将查看作 ...

  9. Django中六个常用的自定义装饰器

    装饰器作用 decorator是当今最流行的设计模式之一,很多使用它的人并不知道它是一种设计模式.这种模式有什么特别之处? 有兴趣可以看看Python Wiki上例子,使用它可以很方便地修改对象行为, ...

  10. ArcGIS RunTime Sdk +WPF 基础地图显示

    1 简单的地图展示 ArcGISRunTime 的平面地图展示主要依赖MapView这个控件,MapView是地图的容器,Map主要是图层的集合 (注:三维场景的显示主要依赖SceneView这个控件 ...