MongoDB

更新日期: 2017-04-03 阅读次数: 8444 分类: mongodb

It always seems impossible until it's done. -- Nelson Mandela

版本

2.0.4

你确定数据落地了么?

MongoDB 的写操作默认是异步写,即,并不保证数据写到磁盘上,需要使用参数制定。

如果不设置 safe=True, 即使插入重复的数据(有唯一键索引的情况下),也没有报错. 重复的数据会被忽略掉,并不更新。

而 mongoshell 下,则会报错。

    > db.large_thumbnail.insert({"md5": 1})
    > db.large_thumbnail.insert({"md5": 1})

E11000 duplicate key error index: imgdb.large_thumbnail.$md5_1 dup key: { : 1.0 }

这里涉及到了一个 safe insert 的概念, 即默认情况下 mongodb 并不保证数据 是存储成功的,而是立刻返回,所以感觉上是插入速度很快。 如果使用 safe insert 模式,则会在数据真正存储成功时才会返回。也才会报出 duplicate key error 这样的错误信息。

http://api.mongodb.org/python/current/api/pymongo/collection.html

设置了 safe=True 之后,就能看到错误信息了

DuplicateKeyError: E11000 duplicate key error index: imgdb.large_thumbnail.$md5_1 dup key: { : BinData }

try:
    collection.insert(doc, safe=True)
except pymongo.errors.DuplicateKeyError, err:
    # code: None
    # args: (u'E11000 duplicate key error index: 
    #       imgdb.original_image.$md5_1  dup key: { : BinData }',)
    # message: E11000 duplicate key error index: 
    #       imgdb.original_image.$md5_1  dup key: { : BinData }
    logging.info(str(err))

Replica Set

如果在生产环境中配置了 Replica Set,并且磁盘空间非常大,例如 10TB, 在运行 rs.initiate 时,你会发现等待了半个小时,都没有执行完该命令。

为什么?

mongodb 认会在 rs.initiate 时默认抢占 5% 的磁盘空间。

需要修改 oplogSize 参数来控制。

pymongo

保证生产环境和开发、测试测试环境的 pymongo 版本一致。

曾遇到的问题:

由 2.1.1 升级到 2.2 时, 发现没有 pymongo.binary.

例如:

import binascii
from pymongo.binary import Binary, BINARY_SUBTYPE

def get_gif(gif_md5):
    md5_binary = binascii.unhexlify(gif_md5)
    img = imgdb.gif.find_one({"md5": Binary(md5_binary, BINARY_SUBTYPE)})
    if img is None:
        return None
    else:
        return img["bytes"]

pymongo 有重连机制

在 mongod 连接不上的情况下,插入时会报出如下异常

error: [Errno 61] Connection refused

经测试,pymongo 有重连机制,mongod 正常后,可以重新正常访问. 所以,重试数据库操作就可以了, 不需要重新建立连接。(相对于 Cassandra)

如何清理过期数据

MongoDB 在删除数据后(remove),不会主动进行存储文件压缩,而是继续 append 数据。
所以删除所腾出来的空间并不会被利用,需要主动压缩。

但是,主动压缩有弊端:

  • 主动压缩会导致 MongoDB 无法访问.
  • 压缩只是标记已删除的空间可以使用,所以进行了压缩,结果就是产生大量的磁盘碎片

在生产环境中,大数据量的情况下,不能接受。

http://stackoverflow.com/questions/5518581/mongodb-data-remove-reclaim-diskspace

MySQL 也是如此,所以图片缓存与永久存储要分离。

即采用临时 collection, 定期 drop 掉即可。可以有效的回收磁盘空间和内存。 在热数据暴涨时,甚至可以每隔一个小时执行一次,清理无用数据,以回收磁盘和内存空间。

内存占用

测试了一下,在 MongoDB 占用了 89% 内存的情况下,MongoDB 会优先为其他程序释放内存(系统内核控制,Least Recently Used (LRU))。

但是,如果热数据暴涨,其他程序还是抢不到内存。http://www.mongodb.org/display/DOCS/Caching (查看内存占用 top , 按照内存占用排序 O n)

l = []
for x in xrange(10000):
    l.append("t" * 1024 * 1024)
    time.sleep(0.01)

关于设计的问题

请先阅读 http://docs.mongodb.org/manual/core/data-modeling/#data-modeling-patterns-and-examples

在看完上面的设计之后,你一定觉得其实自己对 MongoDB 完全不了解。

Documents larger than 4MB (when converted to BSON) cannot be saved to the database. This is a somewhat arbitrary limit (and may be raised in the future); it is mostly to prevent bad schema design and ensure consistent performance.

http://stackoverflow.com/questions/4667597/understanding-mongodb-bson-document-size-limit

MongoDB 是不是真的那么灵活?

我个人觉得不一定,灵活带来的好处是前期可以快速实现简单的 demo。 但是在后期不断涉及到关系型数据的时候,并且有大量新需求的时候,你有可能就会抓狂。 这种情况下,如果你对 MongoDB 的设计完全不了解的话,请使用传统的关系型数据库。

所以,真实项目中,我只用 MongoDB 存储简单的数据结构,例如,图片信息和图片流。 用户信息仍然使用 MySQL 存储。

年初的时候我把自己的博客使用 MongoDB 实现了一遍,但是后来发现在功能扩张时, 只要涉及到关系数据操作的事务性时,我就很头疼。(也跟我对其设计模式的陌生有关)

推荐阅读

为了晚上能安心的睡个好觉,不被报警短信吵醒,推荐大家阅读。

  • MongoDB 官方文档 http://docs.mongodb.org/manual/ 其中 FAQ 必读,这里能解决你绝大部分的疑问 http://docs.mongodb.org/manual/faq/
  • MongoDB and Python http://book.douban.com/subject/6851222/

关于连接数的问题

如果连接数过多,就需要手动去修改每个连接所占用的内存大小。

http://www.mongodb.org/display/DOCS/Checking+Server+Memory+Usage

The size of each stack frame is determined by the stack size; in Linux this typically defaults to 8MB, which means that each connection will use 8MB on the server. If you are using many connections and are concerned with memory usage you should reduce the stack size to 1MB (this is automatic in the upcoming v2.0 release).

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式