温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

理解MongoDB默认的ObjectID

发布时间:2020-06-25 16:43:47 阅读:2628 作者:ztfriend 栏目:MongoDB数据库
GO开发者专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

BSON ObjectID Specification

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
  • TimeStamp. This is a unix style timestamp. It is a signed int representing the number of seconds before or after January 1st 1970 (UTC).
  • Machine. This is the first three bytes of the (md5) hash of the machine host name, or of the mac/network address, or the virtual machine id.
  • Pid. This is 2 bytes of the process id (or thread id) of the process generating the object id.
  • Increment. This is an ever incrementing value, or a random number if a counter can't be used in the language/runtime.

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<ObjectId> , 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<NetworkInterface> 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 );       }   } 

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI

开发者交流群×