zoukankan      html  css  js  c++  java
  • MongoDB学习笔记~ObjectId主键的设计

    回到目录

    说一些关于ObjectId的事

    MongoDB确实是最像关系型数据库的NoSQL,这在它主键设计上可以体现的出来,它并没有采用自动增长主键,因为在分布式服务器之间做数据同步很麻烦,而是采用了一种ObjectId的方式,它生成方便,占用空间比long多了4个字节,(12个字节)在数据表现层面也说的过去,它是一种以时间,机器,进程和自增几个因素组合的方式来体现的,可以近似看成是按时间的先后进行排序的,对于ObjectId的生成我们可以通过MongoDB服务端去获得,或者在客户端也有对它的集成,使用方便,一般情况下,在客户端实体类中只要定义一个ObjectId类型的属性,这个属性就默认被赋上值了,应该说,还是比较方便的,由于它存储是一种字符串,所以,一般客户端,像NoRM都为我们实现了对string类型的隐藏转换,应该说,还是比较友好的!

    ObjectId的组成

    为何选择十六进制表示法

    为什么在ObjectId里,将byte[]数组转为字符串时,使用十六进制而没有使用默认的十进制呢,居 我的研究,它应该是考虑字符串的长度一致性吧,因为byte取值为(0~255),如果使用默认的十进制那么它的值长度非常不规范,有1位,2位和3位, 而如果使用十六进制表示,它的长度都为2位,2位就可以表示0到255中的任何数字了,0对应0x00,255对应0xFF,呵呵,将它们转为字符串后,即可 以保证数据的完整性,又可以让它看上去长度是一致的,何乐不为呢,哈哈!

    漂亮的设计,原自于扎实的基础!

     在C#版的NoRM这样设计ObjectId

            /// <summary>
            /// Generates a byte array ObjectId.
            /// </summary>
            /// <returns>
            /// </returns>
            public static byte[] Generate()
            {
                var oid = new byte[12];
                var copyidx = 0;
                //时间差
                Array.Copy(BitConverter.GetBytes(GenerateTime()), 0, oid, copyidx, 4);
                copyidx += 4;
                //机器码
                Array.Copy(machineHash, 0, oid, copyidx, 3);
                copyidx += 3;
                //进程码
                Array.Copy(procID, 0, oid, copyidx, 2);
                copyidx += 2;
                //自增值
                Array.Copy(BitConverter.GetBytes(GenerateInc()), 0, oid, copyidx, 3);
                return oid;
            }

    完整的ObjectId类型源代码

    它重写的ToString()方法,为的是实现byte[]到string串之间的类型转换,并且为string和ObjectId对象实现 implicit的隐式类型转换,方便开发人员在实际中最好的使用它们,需要注意的是在byte[]中存储的数据都是以十六进制的形式体现的

        /// <summary>
        /// Represents a Mongo document's ObjectId
        /// </summary>
        [System.ComponentModel.TypeConverter(typeof(ObjectIdTypeConverter))]
        public class ObjectId
        {        
            private string _string;
    
            /// <summary>
            /// Initializes a new instance of the <see cref="ObjectId"/> class.
            /// </summary>
            public ObjectId()
            {
            }
    
            /// <summary>
            /// Initializes a new instance of the <see cref="ObjectId"/> class.
            /// </summary>
            /// <param retval="value">
            /// The value.
            /// </param>
            public ObjectId(string value)
                : this(DecodeHex(value))
            {
            }
    
            /// <summary>
            /// Initializes a new instance of the <see cref="ObjectId"/> class.
            /// </summary>
            /// <param retval="value">
            /// The value.
            /// </param>
            internal ObjectId(byte[] value)
            {
                this.Value = value;
            }
    
            /// <summary>
            /// Provides an empty ObjectId (all zeros).
            /// </summary>
            public static ObjectId Empty
            {
                get { return new ObjectId("000000000000000000000000"); }
            }
    
            /// <summary>
            /// Gets the value.
            /// </summary>
            /// <value>The value.</value>
            public byte[] Value { get; private set; }
    
            /// <summary>
            /// Generates a new unique oid for use with MongoDB Objects.
            /// </summary>
            /// <returns>
            /// </returns>
            public static ObjectId NewObjectId()
            {
                // TODO: generate random-ish bits.
                return new ObjectId { Value = ObjectIdGenerator.Generate() };
            }
    
            /// <summary>
            /// Tries the parse.
            /// </summary>
            /// <param retval="value">
            /// The value.
            /// </param>
            /// <param retval="id">
            /// The id.
            /// </param>
            /// <returns>
            /// The try parse.
            /// </returns>
            public static bool TryParse(string value, out ObjectId id)
            {
                id = Empty;
                if (value == null || value.Length != 24)
                {
                    return false;
                }
    
                try
                {
                    id = new ObjectId(value);
                    return true;
                }
                catch (FormatException)
                {
                    return false;
                }
            }
    
            /// <summary>
            /// Implements the operator ==.
            /// </summary>
            /// <param retval="a">A.</param>
            /// <param retval="b">The b.</param>
            /// <returns>The result of the operator.</returns>
            public static bool operator ==(ObjectId a, ObjectId b)
            {
                if (ReferenceEquals(a, b))
                {
                    return true;
                }
    
                if (((object)a == null) || ((object)b == null))
                {
                    return false;
                }
    
                return a.Equals(b);
            }
    
            /// <summary>
            /// Implements the operator !=.
            /// </summary>
            /// <param retval="a">A.</param>
            /// <param retval="b">The b.</param>
            /// <returns>The result of the operator.</returns>
            public static bool operator !=(ObjectId a, ObjectId b)
            {
                return !(a == b);
            }
    
            /// <summary>
            /// Returns a hash code for this instance.
            /// </summary>
            /// <returns>
            /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
            /// </returns>
            public override int GetHashCode()
            {
                return this.Value != null ? this.ToString().GetHashCode() : 0;
            }
    
            /// <summary>
            /// Returns a <see cref="System.String"/> that represents this instance.
            /// </summary>
            /// <returns>
            /// A <see cref="System.String"/> that represents this instance.
            /// </returns>
            public override string ToString()
            {
                if (this._string == null && this.Value != null)
                {
                    this._string = BitConverter.ToString(this.Value).Replace("-", string.Empty).ToLower();
                }
    
                return this._string;
            }
    
            /// <summary>
            /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
            /// </summary>
            /// <param retval="o">
            /// The <see cref="System.Object"/> to compare with this instance.
            /// </param>
            /// <returns>
            /// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
            /// </returns>
            public override bool Equals(object o)
            {
                var other = o as ObjectId;
                return this.Equals(other);
            }
    
            /// <summary>
            /// Equalses the specified other.
            /// </summary>
            /// <param retval="other">
            /// The other.
            /// </param>
            /// <returns>
            /// The equals.
            /// </returns>
            public bool Equals(ObjectId other)
            {
                return other != null && this.ToString() == other.ToString();
            }
    
            /// <summary>
            /// Decodes a HexString to bytes.
            /// </summary>
            /// <param retval="val">
            /// The hex encoding string that should be converted to bytes.
            /// </param>
            /// <returns>
            /// </returns>
            protected static byte[] DecodeHex(string val)
            {
                var chars = val.ToCharArray();
                var numberChars = chars.Length;
                var bytes = new byte[numberChars / 2];
    
                for (var i = 0; i < numberChars; i += 2)
                {
                    bytes[i / 2] = Convert.ToByte(new string(chars, i, 2), 16);
                }
    
                return bytes;
            }
    
            /// <summary>TODO::Description.</summary>
            public static implicit operator string(ObjectId oid)
            {
                return oid == null ? null : oid.ToString();
            }
    
            /// <summary>TODO::Description.</summary>
            public static implicit operator ObjectId(String oidString)
            {
                ObjectId retval = ObjectId.Empty;
                if(!String.IsNullOrEmpty(oidString))
                {
                    retval = new ObjectId(oidString);
                }
                return retval;
            }
        }

    ObjectIdGenerator源代码

    它主要实现了ObjectId串生成的规则及方式

        /// <summary>
        /// Shameless-ly ripped off, then slightly altered from samus' implementation on GitHub
        /// http://github.com/samus/mongodb-csharp/blob/f3bbb3cd6757898a19313b1af50eff627ae93c16/MongoDBDriver/ObjectIdGenerator.cs
        /// </summary>
        internal static class ObjectIdGenerator
        {
            /// <summary>
            /// The epoch.
            /// </summary>
            private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    
            /// <summary>
            /// The inclock.
            /// </summary>
            private static readonly object inclock = new object();
    
            /// <summary>
            /// The inc.
            /// </summary>
            private static int inc;
    
            /// <summary>
            /// The machine hash.
            /// </summary>
            private static byte[] machineHash;
    
            /// <summary>
            /// The proc id.
            /// </summary>
            private static byte[] procID;
    
            /// <summary>
            /// Initializes static members of the <see cref="ObjectIdGenerator"/> class. 
            /// </summary>
            static ObjectIdGenerator()
            {
                GenerateConstants();
            }
    
            /// <summary>
            /// Generates a byte array ObjectId.
            /// </summary>
            /// <returns>
            /// </returns>
            public static byte[] Generate()
            {
                var oid = new byte[12];
                var copyidx = 0;
                //时间差
                Array.Copy(BitConverter.GetBytes(GenerateTime()), 0, oid, copyidx, 4);
                copyidx += 4;
                //机器码
                Array.Copy(machineHash, 0, oid, copyidx, 3);
                copyidx += 3;
                //进程码
                Array.Copy(procID, 0, oid, copyidx, 2);
                copyidx += 2;
                //自增值
                Array.Copy(BitConverter.GetBytes(GenerateInc()), 0, oid, copyidx, 3);
                return oid;
            }
    
            /// <summary>
            /// Generates time.
            /// </summary>
            /// <returns>
            /// The time.
            /// </returns>
            private static int GenerateTime()
            {
                var now = DateTime.Now.ToUniversalTime();
    
                var nowtime = new DateTime(epoch.Year, epoch.Month, epoch.Day, now.Hour, now.Minute, now.Second, now.Millisecond);
                var diff = nowtime - epoch;
                return Convert.ToInt32(Math.Floor(diff.TotalMilliseconds));
            }
    
            /// <summary>
            /// Generate an increment.
            /// </summary>
            /// <returns>
            /// The increment.
            /// </returns>
            private static int GenerateInc()
            {
                lock (inclock)
                {
                    return inc++;
                }
            }
    
            /// <summary>
            /// Generates constants.
            /// </summary>
            private static void GenerateConstants()
            {
                machineHash = GenerateHostHash();
                procID = BitConverter.GetBytes(GenerateProcId());
            }
    
            /// <summary>
            /// Generates a host hash.
            /// </summary>
            /// <returns>
            /// </returns>
            private static byte[] GenerateHostHash()
            {
                using (var md5 = MD5.Create())
                {
                    var host = Dns.GetHostName();
                    return md5.ComputeHash(Encoding.Default.GetBytes(host));
                }
            }
    
            /// <summary>
            /// Generates a proc id.
            /// </summary>
            /// <returns>
            /// Proc id.
            /// </returns>
            private static int GenerateProcId()
            {
                var proc = Process.GetCurrentProcess();
                return proc.Id;
            }
        }

    事实上,通过对NoRm这个MongoDB客户端的学习,让我们的眼界放宽了许多,可能在思考问题时不局限于眼前,对于同一个问题可以会有更多的解决方法了,呵呵!

    回到目录

  • 相关阅读:
    VC++用Recordset MSPersist载入C#DataSet Save出来的xml失败,但载入VC Recordset Save出来的xml则没问题,怎么xml不通用呢?
    观察力、分析问题的能力、与人沟通的能力和资源整合能力
    [导入]有感于神童之神源
    军训系列二:两类人创业不容易成功
    运行微软的SOAP3.0的VC样例Samples30_ADOTM_Client报错,m_pSoapClient>Invoke时直接失败
    About IM software
    [导入][转]好企业是什么样?
    动网论坛v7.0.0SQL版竟然帯病毒!
    CZoneSoft出品: 音频视频在线录制系列之 AV留言本 简介
    递归算法在生成树型结构中,几乎完全属于无稽的算法
  • 原文地址:https://www.cnblogs.com/lori/p/4409399.html
Copyright © 2011-2022 走看看