关于mongodb中ObjecId的一些详解和验证

实际上写这篇文章的时候网站上已经有很多的说明和文档翻译,只是零零总总的很多,然而我写这篇文章的话,就是一个验证和一个总结的作用,而且我这也只能算是定义为一个知识的搬运。这篇主要是参考了(http://blog.nosqlfan.com/html/3511.html),首先的话,先进行一个ObjecId这个值的概念的一个普及。


什么是ObjectId和组成


    在MongoDB中,如果不特别指定,每个文档都会生成一个唯一的ObjectId作为其主键_id的值。这个值是一个看似随机的串。这个串到底是什么值?为什么MongoDB要使用这个值作为默认主键?它内部又包含了什么样的信息?如果你还不了解,就请看下面文章吧。

感谢刘浩@人民搜索的投稿。

    MongoDB中数据的基本单元称为文档(Document)。文档是MongoDB的核心概念,多个键极其关联的值有序的放置在一起便是文档。

在一个特定集合内部,需要唯一的标识文档。因此MongoDB中存储的文档都由一个”_id”键,用于完成此功能。这个键的值可以是任意类型的,默认试ObjectId对象。ObjectId对象的生成思路是本文的主题,也是很多分布式系统可以借鉴的思路。

为了考虑分布式,“_id”要求不同的机器都能用全局唯一的同种方法方便的生成它。因此不能使用自增主键(需要多台服务器进行同步,既费时又费力),因此选用了生成ObjectId对象的方法。

ObjectId使用12字节的存储空间,其生成方式如下:

0 1 2 3 4 5 6 7 8 9 10 11
时间戳 机器ID PID 计数器

前四个字节时间戳是从标准纪元开始的时间戳,单位为秒,有如下特性:

  1. 时间戳与后边5个字节一块,保证秒级别的唯一性;
  2. 保证插入顺序大致按时间排序;
  3. 隐含了文档创建时间;

机器ID是服务器主机标识,通常是机器主机名的散列值。

同一台机器上可以运行多个mongod实例,因此也需要加入进程标识符PID。

前9个字节保证了同一秒钟不同机器不同进程产生的ObjectId的唯一性。后三个字节是一个自动增加的计数器(一个mongod进程需要一个全局 的计数器),保证同一秒的ObjectId是唯一的。同一秒钟最多允许每个进程拥有(256^3 = 16777216)个不同的ObjectId。

总结一下:时间戳保证秒级唯一,机器ID保证设计时考虑分布式,避免时钟同步,PID保证同一台服务器运行多个mongod实例时的唯一性,最后的计数器保证同一秒内的唯一性(选用几个字节既要考虑存储的经济性,也要考虑并发性能的上限)。

“_id”既可以在服务器端生成也可以在客户端生成,在客户端生成可以降低服务器端的压力。


程序测试验证

#mongodb_objectid.py

#!/usr/bin/env python
#-*- coding:utf-8 -*- 

import time
import pymongo

class CollectionObject(object):
    def __init__(self):
        self._conn_ = pymongo.Connection(‘127.0.0.1‘, 27017)
        self._db_ = self._conn_.spider
        self._stmt_ = self._db_.infos

    def save_item(self, args):
        return self._stmt_.save(args)
		
def main():
    item = CollectionObject()

    for i in xrange(0, 10):
        post = {‘title‘: ‘mongodb_objectid‘, ‘i‘: i}
        time.sleep(5)
        print item.save_item(post)

if __name__ == ‘__main__‘:
    main()	


在插入数据的时候,我在测试了插入数据的时候是每隔5秒中插入一次,

item.save_item(post)返回的是插入文档ObjectId值,如下

时间戳   机器ID PID  计数器
52d8e707 e13823 754e c80398
52d8e70c e13823 754e c80399
52d8e711 e13823 754e c8039a
52d8e716 e13823 754e c8039b
52d8e71b e13823 754e c8039c
52d8e720 e13823 754e c8039d
52d8e725 e13823 754e c8039e
52d8e72a e13823 754e c8039f
52d8e72f e13823 754e c803a0
52d8e734 e13823 754e c803a1


下面这个是程序运行时候进程的进程ID:
root     30030 20898  5 16:17 pts/0    00:00:00 python mongodb_objectid.py
进程ID:30030 
因为机器ID对于我们没有太多的验证价值,所以我们来验证一下时间戳和PID,
因为我的mongodb中已经有一定量的数据了,如果为空的话,计数器应该是从
000000开始累加的,现在也不用看了。


验证程序代码:

#!/usr/bin/env python

def main():
    with open(‘./data.txt‘, ‘r‘) as pf: 
        lines = (pf.read().strip()).split(‘\n‘)
        for line in lines:
            line = line.split()
            print int(line[0], 16), int(line[1], 16) 

if __name__ == ‘__main__‘:
    main()

#data.txt数据格式
52d8e707 754e
52d8e70c 754e
52d8e711 754e
52d8e716 754e
52d8e71b 754e
52d8e720 754e
52d8e725 754e
52d8e72a 754e
52d8e72f 754e
52d8e734 754e


#程序运行结果

文档插入时间戳   进程ID

1389946631 30030
1389946636 30030
1389946641 30030
1389946646 30030
1389946651 30030
1389946656 30030
1389946661 30030
1389946666 30030
1389946671 30030
1389946676 30030


PS: 最近也是由于工作的需要在研究学习mongodb,欢迎大家来交流,邮箱:ky150@126.com


关于mongodb中ObjecId的一些详解和验证

上一篇:【故障公告】数据库服务器 CPU 100% 引发全站故障


下一篇:Cron表达式