这里总结下这段时间使用mongo的心得,列出了几个需要注意的地方。

1. 系统参数及mongo参数设置

mongo参数主要是storageEngine和directoryperdb,这两个参数一开始不选定后续就无法再更改。

directoryperdb主要是将数据库分文件夹存放,方便后续的备份及数据迁移。

storageEngine(存储引擎)默认使用的是MMAPv1,推荐使用3.0新加入的引擎wiredTiger。经实际使用wiredTiger占用的磁盘空间是MMAP的1/5,索引大小是其1/2,查询速度也提高很多,更重要的是该引擎提供了document级别的锁,当集合插入或更新数据时不需要阻塞读操作了。唯一的问题是市面上支持该引擎查询的工具不多,MongoVUE无法查到该引擎存储的集合,NosqlManager-mongo可以查到但需要.net环境支持。个人觉得熟悉下mongo command用mongo shell就足够了,所以还是强烈推荐使用wiredTiger引擎。

2. 无需对集合进行水平切分

由于之前一直使用关系型数据库,关系型数据库当单表数据量超大时经常使用的一直方法是对数据表进行分表。在使用mongo时便很自然的觉得这招仍然有用。由于该系统的分表都是动态生成的,做到后面发现这招对mongo带来的性能提升远远抵不过维护成本的增加。

分析一下关系型数据库分表会提高性能的最大原因是很多关系型数据库一张表是一个文件,分表可以避免一个文件过大所造成数据提取速度变慢。但是mongo并不是这样存储的,所以这条并不成立了。

用过的都知道mongo对索引的依赖非常大,如果集合不能一开始就设计好,那后续索引就得写脚本来创建。这里贡献个给mongo大表动态创建索引的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
eval(function () {
  var infos = [];
  var collNames = db.getCollectionNames();
  for (var i = 0; i < collNames.length; i++) {
    var collName = collNames[i];
    var collSize = db.getCollection(collName).count();
    if (collSize > 1000000 && collName.indexOf("info_")==0) {
     db.getCollection(collName).ensureIndex({publishDate:-1,blendedScore:-1,publishTime:-1,isRubbish:1},{name:"ScoreSortIdx",background:true});
      db.getCollection(collName).ensureIndex({similarNum:-1,publishTime:-1,isRubbish:1},{name:"HotSortIdx",background:true});
      db.getCollection(collName).ensureIndex({publishTime:-1,isRubbish:1},{name:"TimeSortIdx",background:true});
      infos.push("name:" + collName + "索引创建成功");
    }
  }
  return infos;
}());

这么看动态创建索引勉强还是可以解决的,但是最坑的一个地方是sharding完全没办法做了。shard需要指定要shard的集合和分区键,这个就没法提前动态指定了。所以mongo集合不需要做水平切分(至少千万级不需要了,更大直接shard掉),只需要按业务分开就可以了。

3. 使用Capped Collection

有人使用mongo做数据缓存,而且是缓存固定数量的数据,仍然用正常的集合,然后定期清理数据。其实这时用capped collection性能会好很多。

4. 生产环境一定要用副本集

很多人线上环境还是用单机版,虽然部署快但是很多mongo自然提供的功能都没有用到像自动故障转移、读写分离,这些对后续系统扩容及性能优化太重要了。我想会使用mongo的应该是数据量达到一定级别,查询性能会非常重要,所以强烈建议上线时直接使用副本集。

5. 学会使用explain

之前一直习惯用工具来查询,现在发现应该多使用mongo shell命令来查询,并使用explain查看查询计划。另外在寻找最优索引的时候hint命令也是非常有用的。

1
db.info.find({publishDate:{$gte:20160310,$lte:20160320},isRubbish:{$in:[0,1]},title:{$regex:".*test.*"},$or:[{useId:10},{groupId:20}]}).explain("executionStats");

6. 写操作频繁无法使用读写分离

由于系统写操作较多,造成各种w级别锁经常出现(这种锁一般是block read的)而且系统对于数据一致性要求不会太多(大多是后台写入,前台读取,因此允许有一定延迟)所以想用副本集来做读写分离。当真正测试后发现副本集上的读取也经常出现阻塞的情况。通过db.currentOp()发现经常出现一个op:none的操作在申请global write lock,这时所有操作的状态都是在waitingForLock:true,这个问题google了很久都没找到解决方法。后面在官方文档有关并发的FAQ中发现下面这个大坑:

How does concurrency affect secondaries?

In replication, MongoDB does not apply writes serially to secondaries. 
Secondaries collect oplog entries in batches and then apply those 
batches in parallel. Secondaries do not allow reads while applying the 
write operations, and apply write operations in the order that they 
appear in the oplog.

原来mongodb的副本在复制主节点数据执行oplog的时候,读取是被阻塞的,这基本宣告无法在副本上去读取数据了,白白耗费了几天精力。所以mongo官方不推荐做读写分离,原来坑是在这里。。。其实写多读少的情况做读写分离作用也不大,因为性能瓶颈主要是在写入,读取一般不消耗多少资源(另外wiredTiger引擎的锁做到了doc级别,所以锁的情况相对较少)。官方推荐的做法是shard,可以有效的将写入分配到多台服务器提高写入速度,使系统实现水平扩容。

7、千万不要让磁盘满了

80%的时候就要开始注意从集拆分片,如果你的数据增长特别快,很可能你还没有拆分磁盘就满了导致MongoDB挂掉了。如果数据量很大,尽量使用分片,不要使用副本集,做好磁盘容量规划,就是使用分片了也提前扩容,毕竟chunk迁移还是那么的慢。

8、安全风险

MongoDB是默认不提示用户设置密码的,所以,如果你没有配置密码又把MongoDB放在公网上面了,那么「恭喜」,你可能已经成为了肉鸡

9、数据库级锁

MongoDB的锁机制和一般关系数据库如 MySQL(InnoDB), Oracle 有很大的差异,InnoDB 和 Oracle 能提供行级粒度锁,而 MongoDB 只能提供 库级粒度锁,这意味着当 MongoDB 一个写锁处于占用状态时,其它的读写操作都得干等。

初看起来库级锁在大并发环境下有严重的问题,但是 MongoDB 依然能够保持大并发量和高性能,这是因为 MongoDB 的锁粒度虽然很粗放,但是在锁处理机制和关系数据库锁有很大差异,主要表现在:

•MongoDB 没有完整事务支持,操作原子性只到单个 document 级别,所以通常操作粒度比较小;

•MongoDB 锁实际占用时间是内存数据计算和变更时间,通常很快;

•MongoDB 锁有一种临时放弃机制,当出现需要等待慢速 IO 读写数据时,可以先临时放弃,等 IO 完成之后再重新获取锁。

通常不出问题不等于没有问题,如果数据操作不当,依然会导致长时间占用写锁,比如下面提到的前台建索引操作,当出现这种情况的时候,整个数据库就处于完全阻塞状态,无法进行任何读写操作,情况十分严重。

解决问题的方法,尽量避免长时间占用写锁操作,如果有一些集合操作实在难以避免,可以考虑把这个集合放到一个单独的 MongoDB 库里,因为 MongoDB 不同库锁是相互隔离的,分离集合可以避免某一个集合操作引发全局阻塞问题。

MongoDB优化心得分享的更多相关文章

  1. 日常分享:关于时间复杂度和空间复杂度的一些优化心得分享(C#)

    前言 今天分享一下日常工作中遇到的性能问题和解决方案,比较零碎,后续会持续更新(运行环境为.net core 3.1) 本次分享的案例都是由实际生产而来,经过简化后作为举例 Part 1(作为简单数据 ...

  2. 百度APP移动端网络深度优化实践分享(一):DNS优化篇

    本文由百度技术团队“蔡锐”原创发表于“百度App技术”公众号,原题为<百度App网络深度优化系列<一>DNS优化>,感谢原作者的无私分享. 一.前言 网络优化是客户端几大技术方 ...

  3. 【创客+】偷心锁屏创始人Jerry创业心得分享

    偷心锁屏创始人Jerry创业心得分享 作者:Jerry权泉,偷心锁屏创始人 我创业的起因非常偶然.08年在东京早稻田大学读博士期间,每周六都去社区活动中心跟日本人志愿者日语对话练习日语.有一次练习结束 ...

  4. 百度APP移动端网络深度优化实践分享(二):网络连接优化篇

    本文由百度技术团队“蔡锐”原创发表于“百度App技术”公众号,原题为<百度App网络深度优化系列<二>连接优化>,感谢原作者的无私分享. 一.前言 在<百度APP移动端网 ...

  5. eclipse使用技巧心得分享

    eclipse使用技巧心得分享   习惯了eclipse开发java程序,公司最近的项目都是idea开发的,同时android studio也是idea原型开发的,在学android开发,所以脱离ec ...

  6. SSE图像算法优化系列二:高斯模糊算法的全面优化过程分享(一)。

    这里的高斯模糊采用的是论文<Recursive implementation of the Gaussian filter>里描述的递归算法. 仔细观察和理解上述公式,在forward过程 ...

  7. MongoDB优化,建立索引实例及索引机制原理讲解

    MongoDB优化,建立索引实例及索引机制原理讲解 为什么需要索引? 当你抱怨MongoDB集合查询效率低的时候,可能你就需要考虑使用索引了,为了方便后续介绍,先科普下MongoDB里的索引机制(同样 ...

  8. Lucene底层原理和优化经验分享(1)-Lucene简介和索引原理

    Lucene底层原理和优化经验分享(1)-Lucene简介和索引原理 2017年01月04日 08:52:12 阅读数:18366 基于Lucene检索引擎我们开发了自己的全文检索系统,承担起后台PB ...

  9. MongoDB优化之二:常见优化方法

    四个方面进行 cpu/io 方面的优化处理: 1.集群架构上进行读写分离.所有查询优先考虑在从库上读取,写操作在主库上执行.避免主库混合读写压力过大,也减少主库上读写记录的锁冲突. connectio ...

随机推荐

  1. MongoDB的使用学习之(四)权限设置--用户名、密码、端口==

    本文参照:http://hi.baidu.com/tianhuimin/item/590d96cfd7ac1509c610b26a 本人也是按照此文章操作的,但是有些不妥,红色文字就是我实践后,需要改 ...

  2. 42. Trapping Rain Water (JAVA)

    Given n non-negative integers representing an elevation map where the width of each bar is 1, comput ...

  3. Linux学习--第四天--find、locate、帮助命令、grep、who、w、压缩命令、网络命令、mount

    find 命令格式:find 搜索范围 匹配条件find 搜索范围 匹配条件(搜索范围一定要填写,不写默认为当前文件夹,不包括子文件夹.) find /etc -name init #搜索文件和文件夹 ...

  4. Nginx优化_数据包头部信息过大问题

    如果客户端发出请求的URL头部信息过大,网站将不能及时响应,并通过状态码414报错. <center><h1>414 Request-URI Too Large</h1& ...

  5. Red Hat Enterprise Linux 8.0 安装

    Red Hat Enterprise Linux 8.0 安装 本次安装通过使用VMware Workstation 15 pro 进行. 1.新建虚拟机 2.点击首页的创建新的虚拟机,或者点击标签栏 ...

  6. hdu4731 Minimum palindrome (找规律)

    这道题找下规律,3个字母或者以上的时候就用abcabcabc....循环即可. 一个字母时,就是aaaaa.....; 当只有2个字母时!s[1][]=a"; s[2][]="ab ...

  7. Codeforces 954 dijsktra 离散化矩阵快速幂DP 前缀和二分check

    A B C D 给你一个联通图 给定S,T 要求你加一条边使得ST的最短距离不会减少 问你有多少种方法 因为N<=1000 所以N^2枚举边数 迪杰斯特拉两次 求出Sdis 和 Tdis 如果d ...

  8. Codeforces 957 水位标记思维题

    A #include <bits/stdc++.h> #define PI acos(-1.0) #define mem(a,b) memset((a),b,sizeof(a)) #def ...

  9. Linux openssh8.0p1升级步骤

    前期准备开启本机telnet服务,以防openssh升级失败无法连接服务器.注:redhat 5 6 和 redhat7 开机启动配置相关文件不同,请注意 1.安装zlibtar -xzvf zlib ...

  10. LeetCode--098--验证搜索二叉树(python)

    给定一个二叉树,判断其是否是一个有效的二叉搜索树. 假设一个二叉搜索树具有如下特征: 节点的左子树只包含小于当前节点的数.节点的右子树只包含大于当前节点的数.所有左子树和右子树自身必须也是二叉搜索树. ...