重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
A BSON ObjectID is a 12-byte value consisting of a 4-byte timestamp (seconds since epoch), a 3-byte machine id, a 2-byte process id, and a 3-byte counter. Note that the timestamp and counter fields must be stored big endian unlike the rest of BSON. This is because they are compared byte-by-byte and we want to ensure a mostly increasing order. The format:
专注于为中小企业提供网站建设、成都网站设计服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业邵阳免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了千余家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
time | machine | pid | inc |
BSON ObjectIds can be any 12 byte binary string that is unique; however, the server itself and almost all drivers use the format above.
分段查看ObjectId的指令及结果如下:
- > db.test.findOne()._id.toString()
- ObjectId("50c6b336ba95d7738d1042e3")
- > db.test.findOne()._id.toString().substring(10,18)
- 50c6b336
- > db.test.findOne()._id.toString().substring(18,24)
- ba95d7
- > db.test.findOne()._id.toString().substring(24,28)
- 738d
- > db.test.findOne()._id.toString().substring(28,34)
- 1042e3
ObjectId占用12字节的存储空间,由“时间戳” 、“机器名”、“PID号”和“计数器”组成。使用机器名的好处是在分布式环境中能够避免单点计数的性能瓶颈。使用PID号的好处是支持同一机器内运行多个mongod实例。最终采用时间戳和计数器的组合来保证唯一性。
时间戳
确保ObjectId唯一性依赖的是时间的顺序,不依赖时间的取值,因此集群节点的时间不必完全同步。既然ObjectId已经有了时间戳,那么在文档中就可以省掉一个时间戳了。在使用ObjectID提取时间时,应注意到MongoDB允许各节点时间不一致这一细节。
下面是查看时间戳的两种写法:
- > db.test1.findOne()._id.getTimestamp()
- ISODate("2012-12-12T03:52:45Z")
- > Date(parseInt(db.test1.findOne()._id.toString().substring(10,18),16))
- Wed Dec 12 2012 12:11:02 GMT+0800
机器名
机器名通过Md5加密后取前三个字节,应该还是有重复概率的,配置生产集群时检查一下总不会错。另外,我也注意到重启MongoDB后MD5加密结果会发生变化,在利用ObjectID提取机器名信息时需格外注意。
PID号
注意到每次重启mongod进程后PID号通常会发生变化就可以了。
计数器
计数器占3个字节,表示的取值范围就是256*256*256-1=16777215。不妨认为MongDB性能的极限是单台设备一秒钟插入一千万条记录。以目前的水平看,单台设备一秒钟插入一万条就很不错了,因此ObjectID计数器的设计是够用的。
循环插入了一些记录,下面的查询中b是循环计数器,可以看出我机器上的ObjectId计数器是按顺序增加的:
- > parseInt(db.test.findOne({b:1000})._id.toString().substring(28,34),16)
- 1947382
- > parseInt(db.test.findOne({b:1001})._id.toString().substring(28,34),16)
- 1947383
- > parseInt(db.test.findOne({b:1002})._id.toString().substring(28,34),16)
- 1947384
- > parseInt(db.test.findOne({b:1003})._id.toString().substring(28,34),16)
- 1947385
以下代码源自:http://www.cnblogs.com/xjk15082/archive/2011/09/18/2180792.html
- 构建objectId
- public class ObjectId implements Comparable
, java.io.Serializable { - final int _time;
- final int _machine;
- final int _inc;
- boolean _new;
- public ObjectId(){
- _time = (int) (System.currentTimeMillis() / 1000);
- _machine = _genmachine;
- _inc = _nextInc.getAndIncrement();
- _new = true;
- }
- ……
- }
- 机器码和进程码的生成
- private static final int _genmachine;
- static {
- try {
- final int machinePiece;
- {
- StringBuilder sb = new StringBuilder();
- Enumeration
e = NetworkInterface.getNetworkInterfaces(); - while ( e.hasMoreElements() ){
- NetworkInterface ni = e.nextElement();
- sb.append( ni.toString() );
- }
- machinePiece = sb.toString().hashCode() << 16;
- LOGGER.fine( "machine piece post: " + Integer.toHexString( machinePiece ) );
- }
- final int processPiece;
- {
- int processId = new java.util.Random().nextInt();
- try {
- processId = java.lang.management.ManagementFactory.getRuntimeMXBean().getName().hashCode();
- }catch ( Throwable t ){
- }
- ClassLoader loader = ObjectId.class.getClassLoader();
- int loaderId = loader != null ? System.identityHashCode(loader) : 0;
- StringBuilder sb = new StringBuilder();
- sb.append(Integer.toHexString(processId));
- sb.append(Integer.toHexString(loaderId));
- processPiece = sb.toString().hashCode() & 0xFFFF;
- LOGGER.fine( "process piece: " + Integer.toHexString( processPiece ) );
- }
- _genmachine = machinePiece | processPiece;
- LOGGER.fine( "machine : " + Integer.toHexString( _genmachine ) );
- }catch ( java.io.IOException ioe ){
- throw new RuntimeException( ioe );
- }
- }