`
冷静
  • 浏览: 146910 次
  • 性别: Icon_minigender_1
  • 来自: 佛山
社区版块
存档分类
最新评论

mongodb objectid

    博客分类:
  • DB
 
阅读更多



 

存储在mongodb集合中的每个文档(document)都有一个默认的主键_id,这个主键名称是固定的,它可以是mongodb支持的任何数据类型,默认是ObjectId。在关系数据库schema设计中,主键大多是数值型的,比如常用的int和long,并且更通常的,主键的取值由数据库自增获得,这种主键数值的有序性有时也表明了某种逻辑。反观mongodb,它在设计之初就定位于分布式存储系统,所以它原生的不支持自增主键。而现实的世界是,大量应用在可预见的时空里并不需要分布式的mongodb,所以网上就出现了大量的实现mongodb自增主键方法的文章。恩,我之前也干过这种事情。

还是看看ObjectId的底细吧。ObjectId被设计成跨机器的分布式环境中全局唯一的类型,长度是12个字节。有朋友可能嘀咕了,这可比int大了两倍,比long也多了一个int,很不经济嘛,但在现在的硬件配置中,多出的这些字节很难有理由成为系统的瓶颈所在,所以尽可能放心使用之。ObjectId的12字节是如此构成的:0-3这4个字节是时间戳(timestamp)、4-6这3个字节是机器码(machine)、7-8两个字节是进程id(pid)、9-11是程序自增id(increment)。可以看下java driver中ObjectId的实现代码:

	public class ObjectId implements Comparable<ObjectId> , java.io.Serializable {
 
    static final boolean D = false;
 
    /** Gets a new object id.
     * @return the new id
     */
    public static ObjectId get(){
        return new ObjectId();
    }
 
    /** Checks if a string could be an <code>ObjectId</code>.
     * @return whether the string could be an object id
     */
    public static boolean isValid( String s ){
        if ( s == null )
            return false;
 
        if ( s.length() < 18 )
            return false;
 
        for ( int i=0; i<s.length(); i++ ){
            char c = s.charAt( i );
            if ( c >= '0' && c <= '9' )
                continue;
            if ( c >= 'a' && c <= 'f' )
                continue;
            if ( c >= 'A' && c <= 'F' )
                continue;
 
            return false;
        }        
 
        return true;
    }
 
    /** Turn an object into an <code>ObjectId</code>, if possible.
     * Strings will be converted into <code>ObjectId</code>s, if possible, and <code>ObjectId</code>s will
     * be cast and returned.  Passing in <code>null</code> returns <code>null</code>.
     * @param o the object to convert 
     * @return an <code>ObjectId</code> if it can be massaged, null otherwise 
     */
    public static ObjectId massageToObjectId( Object o ){
        if ( o == null )
            return null;
 
        if ( o instanceof ObjectId )
            return (ObjectId)o;
 
        if ( o instanceof String ){
            String s = o.toString();
            if ( isValid( s ) )
                return new ObjectId( s );
        }
 
        return null;
    }
 
    public ObjectId( Date time ){
        _time = _flip( (int)(time.getTime() / 1000) );
        _machine = _genmachine;
        synchronized ( _incLock ){
            _inc = _nextInc++;
        }     
        _new = false;
    }
 
    public ObjectId( Date time , int inc ){
        this( time , _genmachine , inc );
    }
 
    public ObjectId( Date time , int machine , int inc ){
        _time = _flip( (int)(time.getTime() / 1000) );
        _machine = machine;
        _inc = inc;
        _new = false;
    }
 
 
    /** Creates a new instance from a string.
     * @param s the string to convert
     * @throws IllegalArgumentException if the string is not a valid id
     */
    public ObjectId( String s ){
        this( s , false );
    }
 
    public ObjectId( String s , boolean babble ){
 
        if ( ! isValid( s ) )
            throw new IllegalArgumentException( "invalid ObjectId [" + s + "]" );
 
        if ( babble )
            s = babbleToMongod( s );
 
        byte b[] = new byte[12];
        for ( int i=0; i<b.length; i++ ){
            b[b.length-(i+1)] = (byte)Integer.parseInt( s.substring( i*2 , i*2 + 2) , 16 );
        }
        ByteBuffer bb = ByteBuffer.wrap( b );
 
        _inc = bb.getInt(); 
        _machine = bb.getInt();
        _time = bb.getInt();
 
        _new = false;
    }
 
    public ObjectId( byte[] b ){
        if ( b.length != 12 )
            throw new IllegalArgumentException( "need 12 bytes" );
        reverse( b );
        ByteBuffer bb = ByteBuffer.wrap( b );
 
        _inc = bb.getInt();            
        _machine = bb.getInt();
        _time = bb.getInt();
    }
 
 
    public ObjectId( int time , int machine , int inc ){
        _time = time;
        _machine = machine;
        _inc = inc;
 
        _new = false;
    }
 
    /** Create a new object id.
     */
    public ObjectId(){
        _time = _gentime;
        _machine = _genmachine;
 
        synchronized ( _incLock ){
            _inc = _nextInc++;
        }
 
        _new = true;
    }
 
    public int hashCode(){
        return _inc;
    }
 
    public boolean equals( Object o ){
 
        if ( this == o )
            return true;
 
        ObjectId other = massageToObjectId( o );
        if ( other == null )
            return false;
 
        return 
            _time == other._time && 
            _machine == other._machine && 
            _inc == other._inc;
    }
 
    public String toStringBabble(){
        return babbleToMongod( toStringMongod() );
    }
 
    public String toStringMongod(){
        byte b[] = toByteArray();
 
        StringBuilder buf = new StringBuilder(24);
 
        for ( int i=0; i<b.length; i++ ){
            int x = b[i] & 0xFF;
            String s = Integer.toHexString( x );
            if ( s.length() == 1 )
                buf.append( "0" );
            buf.append( s );
        }
 
        return buf.toString();
    }
 
    public byte[] toByteArray(){
        byte b[] = new byte[12];
        ByteBuffer bb = ByteBuffer.wrap( b );
        bb.putInt( _inc );
        bb.putInt( _machine );
        bb.putInt( _time );
        reverse( b );
        return b;
    }
 
    static void reverse( byte[] b ){
        for ( int i=0; i<b.length/2; i++ ){
            byte t = b[i];
            b[i] = b[ b.length-(i+1) ];
            b[b.length-(i+1)] = t;
        }
    }
 
    static String _pos( String s , int p ){
        return s.substring( p * 2 , ( p * 2 ) + 2 );
    }
 
    public static String babbleToMongod( String b ){
        if ( ! isValid( b ) )
            throw new IllegalArgumentException( "invalid object id: " + b );
 
        StringBuilder buf = new StringBuilder( 24 );
        for ( int i=7; i>=0; i-- )
            buf.append( _pos( b , i ) );
        for ( int i=11; i>=8; i-- )
            buf.append( _pos( b , i ) );
 
        return buf.toString();
    }
 
    public String toString(){
        return toStringMongod();
    }
 
    public int compareTo( ObjectId id ){
        if ( id == null )
            return -1;
 
        long xx = id.getTime() - getTime();
        if ( xx > 0 )
            return -1;
        else if ( xx < 0 )
            return 1;
 
        int x = id._machine - _machine;
        if ( x != 0 )
            return -x;
 
        x = id._inc - _inc;
        if ( x != 0 )
            return -x;
 
        return 0;
    }
 
    public int getMachine(){
        return _machine;
    }
 
    public long getTime(){
        long z = _flip( _time );
        return z * 1000;
    }
 
    public int getInc(){
        return _inc;
    }
 
    public int _time(){
        return _time;
    }
    public int _machine(){
        return _machine;
    }
    public int _inc(){
        return _inc;
    }
 
    public boolean isNew(){
        return _new;
    }
 
    public void notNew(){
        _new = false;
    }
 
    final int _time;
    final int _machine;
    final int _inc;
 
    boolean _new;
 
    static int _flip( int x ){
        if ( true ){
            byte b[] = new byte[4];
            ByteBuffer bb = ByteBuffer.wrap( b );
            bb.order( ByteOrder.LITTLE_ENDIAN );
            bb.putInt( x );
            bb.flip();
            bb.order( ByteOrder.BIG_ENDIAN );
            return bb.getInt();
        }
        int z = 0;
        z |= ( x & 0xFF ) << 24;
        z |= ( x & 0xFF00 ) << 8;
        z |= ( x & 0xFF00000 ) >> 8;
        z |= ( x & 0xFF000000 ) >> 24;
        return z;
    }
 
    private static int _nextInc = (new java.util.Random()).nextInt();
    private static final String _incLock = new String( "ObjectId._incLock" );
 
    private static int _gentime = _flip( (int)(System.currentTimeMillis()/1000) );
 
    static final Thread _timeFixer;
    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;
                if ( D ) System.out.println( "machine piece post: " + Integer.toHexString( machinePiece ) );
            }
 
            final int processPiece = java.lang.management.ManagementFactory.getRuntimeMXBean().getName().hashCode() & 0xFFFF;
            if ( D ) System.out.println( "process piece: " + Integer.toHexString( processPiece ) );
 
            _genmachine = machinePiece | processPiece;
            if ( D ) System.out.println( "machine : " + Integer.toHexString( _genmachine ) );
        }
        catch ( java.io.IOException ioe ){
            throw new RuntimeException( ioe );
        }
 
        _timeFixer = new Thread("ObjectId-TimeFixer"){
                public void run(){
                    while ( true ){
                        try {
                            Thread.sleep( 499 );
                        }
                        catch ( Exception e ){}
                        _gentime = _flip( (int)(System.currentTimeMillis()/1000) );
                    }
                }
            };
        _timeFixer.setDaemon( true );
        _timeFixer.start();
    }
 
}

对于ObjectId的组成,有一些值得说道的地方:
1、因为ObjectId以时间戳打头,所以它是近似有序的,使得_id的索引插入效率相比普通索引高很多。
2、ObjectId的前9个字节(timestamp+machine+pid)可以保证不同进程生成的ObjectId不会重复,而后3个字节increment又可以保证同一进程内生成的ObjectId不会重复,所以无需怀疑ObjectId的全局唯一性。
3、ObjectId存储是12个字节,但如果应用有需要以可读的方式表现它,就需要将它转成字符串,这需要24字节(每字节转成2字节的16进制表示),这个长度的字符串看起来就有些不让人舒服了,如果是追踪某个_id引发的bug,就需要配上copy+paste的杀招。
4、初涉ObjectId的朋友很容易犯的两个错误:1)是查询时直接使用类似db.collection.find({_id:”xx”})式的代码,结果怎么也查不到明明存在的文档,而正确的写法应该是:db.collection.find({_id:new ObjectId(“xx”)})。2)是集合间有外键关联时,也需要将外键置为ObjectId类型,而不要直接使用上24字节的string。在写与mongodb打交道的CRUD代码时,需要多留意ObjectId和string的转换代码。
5、ObjectId的产生既可以在应用程序端也可以在mongodb端,各种语言的driver都提供了程序端生成ObjectId的方法,不过大多数人徒省事直接交给mongodb做了。但从mongodb的设计哲学来说,ObjectId更应该由客户端生成,毕竟应用层比存储层更容易扩展,并会提高mongodb的插入速度。

 

  • 大小: 40.8 KB
分享到:
评论

相关推荐

    MongoDB ObjectId

    MongoDB ObjectId是一个重要的概念,它是MongoDB数据库中用于唯一标识文档的一个特殊数据类型。ObjectId作为一个12字节的BSON类型数据,其结构包含了多种信息,以确保文档的唯一性和可追溯性。以下是对ObjectId的...

    node-postgres-objectid:使用 NodeJS 通过映射表将 MongoDB ObjectID 添加到 Postgres 行

    由于几乎所有表都对另一个表有某种引用,因此我们需要在增量 Postgres-ID 和新的 MongoDB ObjectId 之间进行映射,以方便更新。 我们的小 NodeJS 后台任务来了! 它使用 Postgres 内置的触发/侦听功能侦听 ...

    joi-object-id:用于验证MongoDB ObjectId格式的Joi扩展

    @ meanie / joi-object-id Joi扩展程序可将ISO日期字符串自动转换为object-id对象特征扩展现有的string验证器将传入值验证为MongoDB ObjectId值自动将值转换为Mongoose ObjectId实例安装# npmnpm install @meanie/...

    alfred-workflow-objectid:解析MongoDB ObjectId对象并从中提取信息,例如时间戳,机器哈希等

    阿尔弗雷德工作流程对象这个Alfred Workflow插件可以解析MongoDB ObjectId对象,并从中提取一些基本信息和重要信息,例如生成时间戳,机器哈希等。 注意:目前仅支持生成时提取。 即将发布的版本将支持其他ObjectId...

    mongo-roid:用于创建伪随机 MongoDB ObjectID 的 NodeJS 模块

    一个用于创建非常难以猜测的 MongoDB ObjectID 的 NodeJS 模块 安装 安装很容易。 只需发出 NPM 安装命令: $ npm install mongo-roid 用法 使用几乎和安装一样简单。 var randomOID = require ( 'mongo-roid' ...

    java查询mongodb中的objectid示例

    在Java中与MongoDB进行交互时,经常需要查询具有特定`ObjectId`的文档。`ObjectId`是MongoDB中用于唯一标识文档的一种数据类型,通常作为每个文档的默认 `_id` 字段。本示例将详细解释如何使用Java来查询具有指定`...

    graphql-scalar-objectid:GraphQLObjectId-mongodb ObjectId的GraphQL标量

    GraphQLObjectId mongodb ObjectId的GraphQL标量类型。例子const { GraphQLObjectType, GraphQLString } = require('graphql')const GraphQLObjectId = require('graphql-scalar-objectid')const UserType = new ...

    hacken:Hacken是一个可以帮助黑客马拉松的人的节点模块,它具有人们从数据库操作到套接字的几乎所有通用功能。 如果您是与MongoDb ObjectId纠缠的人,那么hacken也可以使您更轻松地使用它

    如果您是与MongoDb ObjectId纠缠的人,那么hacken也可以简化工作。内容对象功能isObjValid 检查ObjectId是否有效。 var hacken = require ( 'hacken' ) ;if ( hacken . isObjValid ( objid ) ) {console . log ( ...

    深究从MongoDB的ObjectId中获取时间信息

    在MongoDB中,每个文档都有一个默认的主键字段`_id`,其类型为ObjectId。ObjectId是一个12字节的二进制结构,通常用于唯一标识文档。在本文中,我们将深入探讨如何从ObjectId中提取时间信息。 ObjectId的结构如下:...

    python根据时间生成mongodb的ObjectId的方法

    MongoDB中的每个文档都有一个唯一的标识符,这就是`_id`字段,它默认是`ObjectId`类型。`ObjectId`是由12字节(96位)组成的,其中前4个字节表示创建该`ObjectId`的秒数,接下来3个字节是机器标识符,接着两个字节是...

    .NET Core中反解ObjectId

    在.NET Core中,MongoDB驱动程序提供了对ObjectId的处理,ObjectId是MongoDB中用于标识文档的一个特殊类型。本文将深入探讨如何在.NET Core环境中反解ObjectId,以及它的数据结构和实现思路。 首先,ObjectId的数据...

    mongo-objectid:基于MongoDB规范的ObjectID

    基于MongoDB规范的ObjectID实现 目标是提供正确的MongoDB ObjectID实现,以确保不可能在快速生成时创建类似的条目(如在其他开源软件包中所见) 目录 安装 npm install mongo-objectid 用法 mkdir myproject cd ...

    MongoDB的ObjectId.pdf

    MongoDB 的 ObjectId 是一个关键的数据类型,它在数据库系统中起着至关重要的作用,特别是在文档数据库如 MongoDB 中。ObjectId 是一个 12 字节的唯一标识符,用于确保每条文档都有其独一无二的身份。在深入讨论 ...

    关于C#生成MongoDB中ObjectId的实现方法

    在MongoDB数据库中,ObjectId是用于唯一标识文档的关键字段,它是一个12字节的BSON类型数据。本文将深入探讨如何在C#环境中生成和处理MongoDB的ObjectId。 首先,ObjectId由四部分组成,每部分都有特定的意义: 1. ...

    C#编程指南:MongoDB中的主键ObjectId生成方法与应用

    内容概要:本文详细介绍了如何在C#中生成MongoDB的主键ObjectId,包括ObjectId的结构组成、生成方法以及具体的C#实现方式。同时,文章还提供了MongoDB.Driver的安装方法,并展示了如何调用ObjectId.GenerateNewId()...

    moloquent:扩展的jenssegerslaravel-mongodb库

    问题是相关模型ID存储为字符串(相关文档ObjectID的值) ,而不是MongoDB ObjectID本身。 可以,并且可以很好地工作,直到您想通过使用lookup聚合来加入相关集合()。 在此程序包中,我们更改了相关文档ID的存储...

    Node.js使用MongoDB的ObjectId作为查询条件的方法

    在Node.js环境中,当你使用MongoDB作为数据库时,经常需要处理`ObjectId`作为查询条件的情况。MongoDB在插入新文档时,会自动生成一个唯一的`ObjectId`作为文档的 `_id` 字段,这个字段通常用于唯一标识每条记录。...

    joi-objectid-validator

    "Joi Objectid验证器"是一个专门针对MongoDB ObjectID类型的验证工具,它结合了Joi验证库的功能,使得在TypeScript项目中能够对ObjectID类型的数据进行有效验证。 Joi是一个强大的验证库,它提供了丰富的验证规则和...

Global site tag (gtag.js) - Google Analytics