MongoDB ObjectId的优化

ObjectID,也就是我们在进行insert操作时会自动生成的_id字段。我们经常会看到它,这个字段的组成及其设计思路我们可以参考NoSQLFan之前的文章《MongoDB文档(Document)全局唯一ID的设计思路》。

今天我们想讲一下对这个字段的一些优化,内容主要来源于MongoDB官方文档。

1.使用业务中的唯一ID

_id字段在不指定时是自动生成的,也就是说,我们也可以在insert操作时自己指定它的值。如果你的业务中对每一条数据都有一个唯一ID,那么建议使用这个ID的值作为_id的值。比如存用户信息的collection,那么你可以使用用户的uid作_id的值,这样做有两个好处:

  • 减少原有_id对空间的占用
  • 让你的_id值意义更明确

2.使用自增数据为_id

由于_id字段是会自动创建索引的,所以如果你按上面的方法用业务中的唯一ID作为_id的值,那么它也会创建一个索引。所以如果你的ID值是一个自增长的字段,那么在insert操作修改索引时,就不需要将_id的索引整个加载到内存中了。(实际上自动生成的_id就有这个特性)

3.使用二进制数据,而非编码字符串

如果你打算设置的_id值是一个二进制的串,那么你不需要将它进行16进制编码后存在一个字符串,因为MongoDB支持BinData类型,你可以直接将二进制串作为_id的值进行存储。

而你也可以通过下面一些方法将二进制串转换为可读的字符串

> // mongo shell bindata info:
> help misc
        b = new BinData(subtype,base64str)  create a BSON BinData value
        b.subtype()                         the BinData subtype (0..255)
        b.length()                          length of the BinData data in bytes
        b.hex()                             the data as a hex encoded string
        b.base64()                          the data as a base 64 encoded string
        b.toString()

4.从ObjectId中获取insert时间

如果你使用了默认的ObjectId作为_id值,并且你还存在一个addtime字段保存添加时间,那么你已经在犯错了。因为MongoDB默认的ObjectId本身就保存了一些信息。其中就包括本条记录生成的时间。你只需要通过下面方法就能获取到其中的信息:

> // ObjectId支持的方法
> help misc
        o = new ObjectId()      create a new ObjectId
        o.getTimestamp()        return timestamp derived from first 32 bits of the OID
        o.isObjectId()
        o.toString()
        o.equals(otherid)

5.使用默认_id进行排序

如果使用了默认的ObjectId作为_id值,而你又希望通过insert的时间进行排序查询,那么一切就简单了,你可以直接使用_id字段进行排序,因为它本身是按insert的时间自增生成的。

> // 获取最新加入的10条记录
> db.mycollection.find().sort({id:-1}).limit(10);

参考:http://www.mongodb.org/display/DOCS/Optimizing+Object+IDs

anyShare据说看到好文章不转的人,服务器容易宕机!
          

无觅相关文章插件,快速提升流量

分类 MongoDB, 未分类 · tag ,

  1. “所以如果你的ID值是一个自增长的字段,那么在insert操作修改索引时,就不需要将_id的索引整个加载到内存中了。(实际上自动生成的_id就有这个特性)”
    没看懂这段话

    • 这是和索引内部实现相关的,MongoDB索引是B+ 树结构,如果你在这个结构的末尾添加数据,那么可能只需要修改较少的节点就可以完成了,而如果你在中间添加,可能会引发节点分裂造成上层节点更大范围的变动。建议看一下B+树的具体实现就了解了。

      • 例如:
        db.insert({‘_id’:1, ‘url’: ‘nosqlfan.com’, ‘times’ : 1});
        db.update({‘url’ : ‘wendal.net’},{‘$inc’ : {‘times’ : 1}}, true);
        以上语句,插入的新纪录的_id是ObjectId的,且不可设置(起码我不懂,请指教.)

        • 嗯,是个问题。当不指定_id时是会生成ObjectID作为_id。目前确实没有解决方案。谢谢提醒。

  2. 1. 强烈建议使用默认 ObjectId , 将业务 id 和 数据 id 区分开来,而不是跟什么 uid 混在一起,我们可以通过建 unique 索引之类的保持 uid 的唯一性
    2. ObjectId 的 getTimestamp 是数据入库的时间,而不是业务执行的时间,所以通常情况下,我们还是得添加 addtime 字段的。

    • 1.上面已经说过了,使用业务id的原因不在于如何保证唯一性,而在于不浪费掉ObjectID占用的数据和索引空间
      2.你说得没错,但是不可否认的是,大多数情况下入库时间和业务执行时间是一致的,至少在我遇到过的应用中是。

    • 严格来说,它是自增的,不过不是加1自增的,MongoDB中要实现MySQL的increment功能,可以使用findAndModify方法。