理解MongoDB默认的ObjectID
admin
2023-04-14 03:21:20
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的指令及结果如下:

  1. > db.test.findOne()._id.toString()  
  2. ObjectId("50c6b336ba95d7738d1042e3")  
  3. > db.test.findOne()._id.toString().substring(10,18)  
  4. 50c6b336  
  5. > db.test.findOne()._id.toString().substring(18,24)  
  6. ba95d7  
  7. > db.test.findOne()._id.toString().substring(24,28)  
  8. 738d  
  9. > db.test.findOne()._id.toString().substring(28,34)  
  10. 1042e3 

ObjectId占用12字节的存储空间,由“时间戳” 、“机器名”、“PID号”和“计数器”组成。使用机器名的好处是在分布式环境中能够避免单点计数的性能瓶颈。使用PID号的好处是支持同一机器内运行多个mongod实例。最终采用时间戳和计数器的组合来保证唯一性。

时间戳

确保ObjectId唯一性依赖的是时间的顺序,不依赖时间的取值,因此集群节点的时间不必完全同步。既然ObjectId已经有了时间戳,那么在文档中就可以省掉一个时间戳了。在使用ObjectID提取时间时,应注意到MongoDB允许各节点时间不一致这一细节。

下面是查看时间戳的两种写法:

  1. > db.test1.findOne()._id.getTimestamp()
  2. ISODate("2012-12-12T03:52:45Z")
  3. > Date(parseInt(db.test1.findOne()._id.toString().substring(10,18),16))
  4. 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计数器是按顺序增加的:

  1. > parseInt(db.test.findOne({b:1000})._id.toString().substring(28,34),16)  
  2. 1947382  
  3. > parseInt(db.test.findOne({b:1001})._id.toString().substring(28,34),16)  
  4. 1947383  
  5. > parseInt(db.test.findOne({b:1002})._id.toString().substring(28,34),16)  
  6. 1947384  
  7. > parseInt(db.test.findOne({b:1003})._id.toString().substring(28,34),16)  
  8. 1947385 

以下代码源自:http://www.cnblogs.com/xjk15082/archive/2011/09/18/2180792.html

  1. 构建objectId   
  2.  public class ObjectId implements Comparable , java.io.Serializable {  
  3.  final int _time;  
  4.      final int _machine;  
  5.      final int _inc;  
  6.  boolean _new;  
  7.    
  8.  public ObjectId(){  
  9.          _time = (int) (System.currentTimeMillis() / 1000);  
  10.          _machine = _genmachine;  
  11.          _inc = _nextInc.getAndIncrement();  
  12.          _new = true;  
  13.  }  
  14.  ……  
  15.  } 
  1. 机器码和进程码的生成  
  2.  private static final int _genmachine;  
  3.  static {  
  4.  try {  
  5.  final int machinePiece;  
  6.          {  
  7.  StringBuilder sb = new StringBuilder();  
  8.              Enumeration e = NetworkInterface.getNetworkInterfaces();  
  9.              while ( e.hasMoreElements() ){  
  10.                  NetworkInterface ni = e.nextElement();  
  11.                  sb.append( ni.toString() );  
  12.              }  
  13.              machinePiece = sb.toString().hashCode() << 16;  
  14.              LOGGER.fine( "machine piece post: " + Integer.toHexString( machinePiece ) );  
  15.  }  
  16.  final int processPiece;  
  17.          {  
  18.              int processId = new java.util.Random().nextInt();  
  19.              try {  
  20.  processId = java.lang.management.ManagementFactory.getRuntimeMXBean().getName().hashCode();  
  21.  }catch ( Throwable t ){  
  22.  }  
  23.  ClassLoader loader = ObjectId.class.getClassLoader();  
  24.              int loaderId = loader != null ? System.identityHashCode(loader) : 0;  
  25.  StringBuilder sb = new StringBuilder();  
  26.              sb.append(Integer.toHexString(processId));  
  27.              sb.append(Integer.toHexString(loaderId));  
  28.              processPiece = sb.toString().hashCode() & 0xFFFF;  
  29.              LOGGER.fine( "process piece: " + Integer.toHexString( processPiece ) );  
  30.          }  
  31.  _genmachine = machinePiece | processPiece;  
  32.          LOGGER.fine( "machine : " + Integer.toHexString( _genmachine ) );  
  33.      }catch ( java.io.IOException ioe ){  
  34.          throw new RuntimeException( ioe );  
  35.      }  
  36.  } 

 

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...