专门整理了一下我用到的监控命令和工具, 见于 : https://my.oschina.net/340StarObserver/blog/793897
当面临性能问题的时候,首先考虑的是优化文档结构和优化索引,其次才是系统调优和硬件优化 在设计文档的结构的时候,必定要很是清除你须要解决的最重要的问题是什么 mongodb并非万能的许愿机,不少时候为了让你的最主要的业务流畅,你只能作出权衡 下面提出两个原则 : 1. 文档结构并不取决于数据的内容,而是取决于数据的访问方式 即,不要面向数据去设计数据库,而是面向应用去设计数据库 2. 机关用尽时,割肉全身,善用冗余来提升查询效率 下面举两个案例 两个案例均来自imooc上面的MongoDB2014北京大会,连接为 http://www.imooc.com/learn/255 案例一 存储电商的产品,每一个文档形如 : { _id : 一个很长的字符串(全局惟一), china : { 该产品的中文描述的具体的一些属性 }, english : { 该产品的英文描述的具体的一些属性 }, japan : { 该产品的日文描述的具体的一些属性 }, ...还有其余十几个语系 } 原始的索引是 { _id : 1 } 原始的查询语句是 db.products.find( { _id : xxx }, { china : 1 } ) 好比这里是一个中国人来查询xxx这件商品的具体信息 乍看之下这个设计确实没问题啊,为何会很慢 ? 通过查日志发现,缺页中断太多,诶 ? 这关缺页中断什么事 ? 缘由以下 : 1. 首先,mongodb在查询的时候,无论你有没有指定是否只返回某些字段,它都会把整个文档查出来放到内存中 2. 只不过在mongodb客户端(mongo-shell或者应用程序)看来,它只拿其中的china字段(固然_id是必拿的) 3. 因此,就算你只须要china字段,实际上内存中是存在着这个商品的所有信息 4. 假设总共20个语系,即内存的使用率只有 5% 5. 换句话说,内存原能够放20个商品,结果放了5个就满了,那当查询其他15个商品时,缺页中断率不高才怪 解决方案 : 拆分文档,拆分后的结构形如 : { _id : 原来的_id 链接上 语系, detail : { 该产品的在该语系下的详细描述 } } 索引不变,如今的查询语句变为 db.products.find({ _id : xxxchina }) 这样一来,mongodb查出来的就是我要用的,内存使用率提升了20倍,缺页中断大大减小 案例二 社交圈中的动态,涉及两种文档,一是用户文档,二是动态文档。简单表示以下 : 用户文档 : { _id : 用户id, friends : [ 朋友1的id, 朋友2的id, ... ] } 用户文档的索引 { _id : 1 } 动态文档 : { creator : 发布者的id, time : 发布的时间戳, content : 动态的内容 } 动态文档的索引 { creator : 1, time : -1 } 要查出个人全部朋友最近的几条动态,须要作联合查询,并且是带in的联合查询,慢是正常的 解决方案 : 在每发布一条动态的时候,顺带加上当前个人全部朋友,修改后的动态文档的结构 : { creator : 发布者的id, time : 发布的时间戳, friends : [ 此时的朋友1的id, 此时的朋友2的id, ... ], content : 动态的内容 } 另外为这个文档增长索引 { friends : 1, time : -1 } 如今的查询语句就不是联合查询了,会快不少 可是这个解决方案很奇葩是么,搞这么大的冗余,多浪费磁盘啊,并且一致性的维护怎么作? 可是,须要牢记的是,你的目标是啥?你须要解决的最大的问题是啥? 若是你的应用,容忍必定程度的不一致性,好比社交圈这种,确定是要优先照顾读的性能,经过冗余来提升读性能是很常见的 你可能会有疑问 : 案例一是由于要查的信息占整个文档的比例过小,因此内存利用率不高致使缺页中断频繁 照这个道理,案例二中增长了很大的冗余信息,不是也会致使频繁的缺页中断吗,为何案例二就能够用冗余呢 此二者不是很矛盾吗? 我一开始也有这个疑惑,可是我要说的是,你会这么想,是不自觉地犯了根据数据内容来设计数据库的老病 具体分析一下 : 案例一存储的是电商商品的信息,全部人均可能会去查询一些热门商品的信息 也就是说,商品信息存在热数据,并且是被全部人均可能访问的热数据 因此,在这种应用场景下,为了让热数据使用率更高,固然要提升内存利用率,即减小缺页中断的次数 既然要提升内存的利用率,就要使得用户查的信息占整个文档的比例尽量大 这样一来,才能让固定的内存去装的下更多数量的文档 因此,须要把一个商品的文档按照语系来切分红不一样的文档 这样一来,那些罕见语系的商品信息基本不会占用内存,内存中都是热门商品的经常使用语系的信息 案例二存储的是不一样用户发表的动态信息 第一,不一样人的动态是不一样的。第二,并且通常查的时候是翻页来查的 这两个缘由就决定了这种数据是不存在热数据的概念的 因此,绝大多数状况,都是不可避免的缺页中断,都是去磁盘上查 既然没法使用热数据,既然不可避免地查磁盘,那就只能让查磁盘更快这条路了 因此,使用冗余是合适的 对比分析了这两个案例,以及你可能出现的疑惑,相信你有了更深入的认识 再次强调 : 不要面向数据去设计数据库,而是要面向应用去设计数据库
和通常的sql索引很相似,不作太多阐述,这边我只提一个以前没有注意到的降序索引 例如消息文档 : { myid : 个人id, who : 谁给我发的消息, time : 消息的时间戳, content : 消息的内容 } 消息文档的索引 { myid : 1, time : -1 } 这样的索引,能够很快地找出与我相关的最近几条消息,实现按时间降序分页查询 若是用{ myid : 1, time : 1 }索引的话 : 还须要排序,我不知道mongodb内部会不会作什么优化,可是用降序索引确定是确保效率的 另外,当你有多种索引方案备选时,建议经过explain来看看谁更好
首先,提两个概念 概念一. 内存映射 mongodb采用内存映射的方式来进行内存和磁盘上的数据调度 其实内存映射是操做系统自带的,并非mongodb本身实现的 当你访问的数据在内存中的时候,mongodb可以很快地把数据拿给你 当你访问的数据不在内存时,会触发缺页中断,告诉操做系统把xx号页面调入内存,而后再读内存 概念二. 工做集 工做集 = 索引 + 内存中的热数据 如何查索引的大小? 在mongo shell中,切换到你的某个库,经过 db.stats() 来查看 其中的 indexSize 即是该库的全部索引占的字节数 如何查内存中的热数据的大小? 在mongo shell中,切换到admin库,经过 db.serverStatus() 来查看 该命令会返回最近15分钟内的运行状况 若是只须要查热数据的大小,能够经过 db.serverStatus()['extra_info'],个人mongo版本是3.2.4 其中的 heap_usage_bytes 即是内存中的热数据占的字节数 为了让mongodb尽可能少的发生缺页中断,应该让内存大于工做集的大小 这即是选用内存大小的根据 若是单机确实没法知足的话,就只能经过分片了 由于不少时候工做集确实很大,甚至索引的大小就超过了实际数据的大小 可是在分片的每台主机上,选用内存的依据仍是工做集
对于不一样的数据,采用不一样的存储介质sql
数据文件用SSD来存储 mongodb绝大多数状况下,都是随机读写 相同状况下,SSD随机读写的效率是HDD的几十倍 日志文件用HDD来存储 对于顺序读写来讲,SSD和HDD的效率相差不大
设置合理的预读值mongodb
什么是预读值? 在请求磁盘上的数据的时候,操做系统并非只返回一个页面,而是把接下来的好几个页面都调入内存中 而mongodb绝大多数是随机读写,若是预读值太大(默认是256),会致使缺页中断比较多 此时就须要调小预读值,通常设置为32比较合适 例如 : 经过 sudo blockdev --report 来查看系统各存储设备的预读值 经过 sudo blockdev --setra 32 /dev/sda5 来设置/dev/sda5这个分区的预读值是32K
关闭数据库文件的atimeshell
禁止系统对文件的访问时间更新会有效提升文件读取的性能 好比你的mongodb的数据在 /mydata 下面,文件系统是 ext4 能够在 /etc/fstab 这个文件中添加一行 /dev/xvdb /mydata ext4 noatime 0 0 修改完后,从新挂载 sudo mount -o remount /mydata
我专门写了一篇关于mongodb的分片片键的博客 [地址是](http://my.oschina.net/340StarObserver/blog/688232)
1. 开启 releaseConnectionsAfterResponse 参数 db.runCommand({ setParameter : 1, releaseConnectionsAfterResponse : true })