MongoDB 陷阱与诀窍

目录 [−]

  1. 读写锁
  2. 内存映射文件
  3. 数据文件
  4. 数据复制有限制
  5. 主从复制不会确保高可用性
  6. 不要使用32位版本
  7. 索引
  8. explain
  9. Spring Data Mongo
    1. 无法检索部分字段,只能获取整个文档
    2. 没有DBRef延迟加载
    3. 不支持游标
    4. 不完备的聚合框架支持
    5. 不完善的索引支持
    6. 无法切换数据库
    7. 不支持文档的动态特性
  10. 参考文档

本文收集了应用MongoDB(以及Spring-data-mongo)的一些技巧,坑和诀窍。 排名不分先后,文末附有出处。

读写锁

MongoDB使用 readers-writer lock来实现并发读而独占写。

自2.2 (事实上2.1.1+)开始实现每个database一个锁(https://jira.mongodb.org/browse/SERVER-4328),同时还有一个全局(global)锁。 以前的版本只有全局锁。
使用下面的方式可以查看锁状态:

  • db.serverStatus(),
  • db.currentOp(),
  • mongotop,
  • mongostat, and/or
  • the MongoDB Management Service (MMS)

更多信息查看: http://docs.mongodb.org/manual/faq/concurrency/

2.7.8版本实现了基于collection的锁。 https://jira.mongodb.org/browse/SERVER-1240

内存映射文件

MongoDB使用mmap系统调用为数据建立内存映射文件。它使用内存管理处理所有的数据。 如果数据没有被映射到内存文件,那么此数据不能被访问。 MongoDB追求的就是快。
但这也带来页缺失(page fault)的问题。
当要访问的数据不在内存中的时候,就发生页缺失的现象。这将严重影响性能。 在页缺失的情况下执行一个操作可能比正常操作花费4万倍的时间。
使用SSD可以较好的提高性能。

数据文件

MongoDB默认的数据文件在/data/db文件中。包括以下一些文件

  • 预分配的文件 (Preallocated data files): 文件名一般是.0,.1等。 第一个文件64M,第二个128M,最大2G, 之后分配的都是2G。它的主要功能就是避免磁盘碎片。 但是...但是对于一个大的数据库, 它的消耗实在是太大了。如果你正在设计一个大的数据库系统,你必须考虑到这一点。目前有一个商业解决方案叫TokuMX,使用后存储消耗将会减少90%。repairDatabase与compact命令多少也会帮到你。
  • 日志文件(oplog):
  • journal: 在Mongo应用写操作之前记录往硬盘的写操作
  • 空记录: MongoDB记录删除的文档和collection,但是不会主动将它们的空间释放。使用compact进行碎片整理,使用repairDatabase释放空间。

数据复制有限制

MongoDB中数据复制的复制集策略非常棒,很容易配置并且使用起来确实不错。但如果集群的节点有12个以上,那么你就会遇到问题。MongoDB中的复制集有12个节点的限制,这里是问题的描述,你可以追踪这个问题看看是否已经被解决了。

主从复制不会确保高可用性

尽管已经不建议被使用了,不过MongoDB还是提供了另外一种复制策略,即主从复制。它解决了12个节点限制问题,不过却产生了新的问题:如果需要改变集群的主节点,那么你必须得手工完成,感到惊讶?看看这个链接吧。

不要使用32位版本

MongoDB的32位版本也是不建议被使用的,因为你只能处理2GB大小的数据。还记得第一个限制么?这是MongoDB关于该限制的说明

索引

ensureIndex() 创建索引
db.collection.getIndexes() 查看索引
db.collection.stats() 查看索引的占用

内存应该足够大到容纳整个索引,否则可能影响性能。

explain

使用explain查找语句的问题
db.XXXXXX.explain()

Spring Data Mongo

Prashant Deva是Chronon的创始人与CTO。Deva喜欢尝试各种新鲜技术,在使用了Spring Data MongoDB一段时间后,他认为这个框架在设计上存在着严重的问题,并撰写了文章进行了深入且详尽的分析。

Spring框架开发者们喜欢到处宣扬他们对MongoDB提供的支持,并以此作为优于其他框架的一个资本。不过,如果在真正的项目中使用Spring Data MongoDB的话,你就会发现框架在某些特性上的严重缺失以及设计上的巨大失误。

无法检索部分字段,只能获取整个文档

这是Spring Data MongoDB设计上最大的瑕疵。它试图用SQL数据库中的行对文档进行建模,并且希望你像ORM一样创建“实体”类。这两者根本就不是一回事。文档可要比SQL数据库中的行复杂多了,也大多了。

没有DBRef延迟加载

这个就更加疯狂了。如果通过DBRefs来引用其他文档,那么Spring Data MongoDB就会得到整个文档而不是文档引用。如果通过DBRefs连接了大量文档,那么一个只想获得几个字段的简单查询都会导致获取到整个文档图!

貌似解决了: DATAMONGO-348

不支持游标

想要通过游标遍历集合吗?没门。要么就取出整个集合,要么就转而使用原生的Mongo Java驱动。

不完备的聚合框架支持

Spring Data MongoDB是从不久之前才开始支持MongoDB聚合框架的,就像框架的其他部分一样,这种支持也是个半成品。

不完善的索引支持

虽然可以通过框架将某个字段标记为“加索引的”,但其他操作却根本就不知道这回事。

无法切换数据库

如果使用原生的Mongo驱动,那么切换数据库简直就是小菜一碟,直接调用getDb(dbName)就行了。但如果使用Spring Data MongoDb,那么这几乎是一件无法做到的事情,除非你愿意自己写大量的代码(不过这么做的话在新版框架发布后可能就会出现移植性问题)。

不支持文档的动态特性

使用MongoDB这样的面向文档的数据库最大的原因就在于其动态特性了。比如说同一集合中的文档可以不同,这样同一个集合中就可以有多个版本的文档,然后逐渐升级,文档也可以嵌套。键名不必事先知道,这样就可以直接向文档中插入属性映射。

但事实却是Spring Data MongoDB丢弃了MongoDB的这个最基本的特性,并且试图在其上构建一个确定的、预先定义好的ORM风格的层,这直接造成框架与底层数据库之间的不匹配。

参考文档

  1. http://www.slideshare.net/vladimirmalyk/mongo-performance-tips-and-tricks
  2. http://www.slideshare.net/YannCluchey/concurrency-patterns-with-mongo-db
  3. http://www.infoq.com/cn/news/2013/11/mongodb-things
  4. http://www.infoq.com/cn/news/2013/11/spring-data-mongo-mismatch