首先這裡不做GUID與整形作為主鍵的優劣之爭,GUID自有它優勢,但GUID本身是亂序的,會對索引的維護帶來性能上的損耗,數據量越大越明顯。
COMB 類型 GUID 是由Jimmy Nilsson在他的“The Cost of GUIDs as Primary Keys”一文中設計出來的。
基本設計思路是這樣的:既然GUID數據因毫無規律可言造成索引效率低下,影響了系統的性能,那麼能不能通過組合的方式,保留GUID的前10個字節,用後6個字節表示GUID生成的時間(DateTime),這樣我們將時間信息與GUID組合起來,在保留GUID的唯一性的同時增加了有序性,以此來提高索引效率。
在NHibernate已經有代碼實現,基於1/300秒為單位生成的,但實際Windows操作系統時鐘更新頻率多核15ms(1/67秒),單核10ms,是無法精確到1/300秒的,而且在短時間內生成大量CombGuid時,DateTime.Now有可能是不變化的,我在測試中遇到了這種情況,結果生成了5萬條CombGuid記錄,其中有兩萬多條是重復的,這樣在實際生產環境中如果數據插入量非常大而且頻繁的情況下,CombGuid排序的優勢就沒有了。
針對NHibernate的代碼進行了些優化,已當前時間1/10000秒為基礎創建CombGuid,如果當前時間基數與上次創建時相同,直接累加1,確保不產生重復的CombGuid:
private static readonly DateTime _CombBaseDate = new DateTime(1900, 1, 1); private static Int32 _LastDays; // 天數 private static Int32 _LastTenthMilliseconds; // 單位:1/10 毫秒 private static readonly Int32 _MaxTenthMilliseconds = 863999999; #region - NewComb - /// <summary>初始化 CombGuid 結構的新實例。</summary> /// <returns>一個新的 CombGuid 對象。</returns> public static CombGuid NewComb() { var guidArray = Guid.NewGuid().ToByteArray(); var now = DateTime.Now; // Get the days and milliseconds which will be used to build the Byte String var days = new TimeSpan(now.Ticks - _CombBaseDate.Ticks).Days; var tenthMilliseconds = (Int32)(now.TimeOfDay.TotalMilliseconds * 10D); var lastDays = _LastDays; var lastTenthMilliseconds = _LastTenthMilliseconds; if (days > lastDays) { Interlocked.CompareExchange(ref _LastDays, days, lastDays); Interlocked.CompareExchange(ref _LastTenthMilliseconds, tenthMilliseconds, lastTenthMilliseconds); } else { if (tenthMilliseconds > lastTenthMilliseconds) { Interlocked.CompareExchange(ref _LastTenthMilliseconds, tenthMilliseconds, lastTenthMilliseconds); } else { if (_LastTenthMilliseconds < Int32.MaxValue) { Interlocked.Increment(ref _LastTenthMilliseconds); } tenthMilliseconds = _LastTenthMilliseconds; } } // Convert to a Byte array var daysArray = BitConverter.GetBytes(days); var msecsArray = BitConverter.GetBytes(tenthMilliseconds); // Reverse the bytes to match SQL Servers ordering Array.Reverse(daysArray); Array.Reverse(msecsArray); // Copy the bytes into the guid Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2); //Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4); Array.Copy(msecsArray, 0, guidArray, guidArray.Length - 4, 4); return new CombGuid(guidArray, true); } #endregion
這樣理論上一天之內允許生成 864000000 個不重復的CombGuid;如果當天生成的個數大於 864000000 ,會一直累加 1 直到 2147483647 ,也就是說實際一天之內能生成 2147483647 個不重復的CombGuid。
完整CombGuid代碼,參考了.net fx內部guid、sqlguid的部分方法:
/* * CombGuid * * 作者:海洋餅干 * 時間:2014-09-10 18:42:51 * 博客:http://www.cnblogs.com/hmking/ * 版權:版權所有 (C) Eme Development Team 2014 */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data.SqlTypes; using System.Runtime.InteropServices; using System.Threading; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; namespace HmFramework { /// <summary>COMB 類型 GUID,要存儲在數據庫中或要從數據庫中檢索的 GUID。</summary> /// <remarks>COMB 類型 GUID 是由Jimmy Nilsson在他的“The Cost of GUIDs as Primary Keys”一文中設計出來的。 /// <para>基本設計思路是這樣的:既然GUID數據因毫無規律可言造成索引效率低下,影響了系統的性能,那麼能不能通過組合的方式, /// 保留GUID的前10個字節,用後6個字節表示GUID生成的時間(DateTime),這樣我們將時間信息與GUID組合起來, /// 在保留GUID的唯一性的同時增加了有序性,以此來提高索引效率。</para> /// <para>也許有人會擔心GUID減少到10字節會造成數據出現重復,其實不用擔心, /// 後6字節的時間精度可以達到 1/10000 秒,兩個COMB類型數據完全相同的可能性是在這 1/10000 秒內生成的兩個GUID前10個字節完全相同,這幾乎是不可能的!</para> /// <para>理論上一天之內允許生成 864000000 個不重復的CombGuid;如果當天生成的個數大於 864000000 ,會一直累加 1 直到 2147483647 , /// 也就是說實際一天之內能生成 2147483647 個不重復的CombGuid。</para> /// </remarks> public struct CombGuid : INullable, IComparable, IComparable<CombGuid>, IEquatable<CombGuid>, IXmlSerializable { #region -- Fields -- private static readonly String _NullString = "nil"; private static readonly Int32 _SizeOfGuid = 16; // Comparison orders. private static readonly Int32[] _GuidComparisonOrders = new Int32[16] { 10, 11, 12, 13, 14, 15, 8, 9, 6, 7, 4, 5, 0, 1, 2, 3 }; // Parse orders. private static readonly Int32[] _GuidParseOrders32 = new Int32[32] { 30, 31, 28, 29, 26, 27, 24, 25, 22, 23, 20, 21, 18, 19, 16, 17, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; private static readonly Int32[] _GuidParseOrders36 = new Int32[36] { 34, 35, 32, 33, 30, 31, 28, 29, 27, 25, 26, 23, 24, 22, 20, 21, 18, 19, 17, 13, 14, 15, 16, 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; // the CombGuid is null if m_value is null private Byte[] m_value; #endregion #region -- 屬性 -- /// <summary>CombGuid 結構的只讀實例,其值空。</summary> public static readonly CombGuid Null = new CombGuid(true); /// <summary>獲取 CombGuid 結構的值。 此屬性為只讀。</summary> public Guid Value { get { if (IsNull) { throw new SqlNullValueException(); } else { return new Guid(m_value); } } } /// <summary>獲取 CombGuid 結構的日期時間屬性。 /// <para>如果同一時間批量生成了大量的 CombGuid 時,返回的日期時間是不准確的!</para> /// </summary> public DateTime DateTime { get { if (IsNull) { throw new SqlNullValueException(); } else { var daysArray = new Byte[4]; var msecsArray = new Byte[4]; // Copy the date parts of the guid to the respective Byte arrays. Array.Copy(m_value, m_value.Length - 6, daysArray, 2, 2); Array.Copy(m_value, m_value.Length - 4, msecsArray, 0, 4); // Reverse the arrays to put them into the appropriate order Array.Reverse(daysArray); Array.Reverse(msecsArray); // Convert the bytes to ints var days = BitConverter.ToInt32(daysArray, 0); var msecs = BitConverter.ToInt32(msecsArray, 0); var date = _CombBaseDate.AddDays(days); if (msecs > _MaxTenthMilliseconds) { msecs = _MaxTenthMilliseconds; } msecs /= 10; return date.AddMilliseconds(msecs); } } } #endregion #region -- 構造 -- /// <summary>實例化一個空 CombGuid 結構</summary> private CombGuid(Boolean isNull) { m_value = null; } /// <summary>使用指定的字節數組初始化 CombGuid 結構的新實例。</summary> /// <param name="value">包含初始化 CombGuid 的值的 16 元素字節數組。</param> public CombGuid(Byte[] value) : this(value, false) { } /// <summary>使用指定的字節數組初始化 CombGuid 結構的新實例。</summary> /// <param name="value">包含初始化 CombGuid 的值的 16 元素字節數組。</param> /// <param name="isOwner">是否 CombGuid 結構直接使用</param> private CombGuid(Byte[] value, Boolean isOwner) { if (value == null || value.Length != _SizeOfGuid) { throw new ArgumentException("value 的長度不是 16 個字節。"); } if (isOwner) { m_value = value; } else { m_value = new Byte[_SizeOfGuid]; value.CopyTo(m_value, 0); } } /// <summary>使用指定字符串所表示的值初始化 CombGuid 結構的新實例。</summary> /// <param name="comb">包含下面任一格式的 CombGuid 的字符串(“d”表示忽略大小寫的十六進制數字): /// <para>32 個連續的數字 dddddddddddddddddddddddddddddddd </para> /// <para>- 或 - </para> /// <para>12 和 4、4、4、8 位數字的分組,各組之間有連線符,dddddddddddd-dddd-dddd-dddd-dddddddd</para> /// </param> public CombGuid(String comb) { ValidationHelper.ArgumentNullOrEmpty(comb, "comb"); Int32 a; Int16 b, c; Byte[] d; if (new GuidParser(comb).TryParse(out a, out b, out c, out d)) { m_value = new Byte[_SizeOfGuid]; Init(a, b, c, d); } else { throw CreateFormatException(comb); } } private static Exception CreateFormatException(String s) { return new FormatException(String.Format("Invalid CombGuid format: {0}", s)); } /// <summary>使用指定的 Guid 參數初始化 CombGuid 結構的新實例。</summary> /// <param name="g">一個 Guid</param> public CombGuid(Guid g) { m_value = g.ToByteArray(); } /// <summary>使用指定的整數和字節數組初始化 CombGuid 類的新實例。</summary> /// <param name="a">CombGuid 的開頭四個字節。</param> /// <param name="b">CombGuid 的下兩個字節。</param> /// <param name="c">CombGuid 的下兩個字節。</param> /// <param name="d">CombGuid 的其余 8 個字節</param> public CombGuid(Int32 a, Int16 b, Int16 c, Byte[] d) { ValidationHelper.ArgumentNull(d, "d"); // Check that array is not too big if (d.Length != 8) { throw new ArgumentException("d 的長度不是 8 個字節。"); } m_value = new Byte[_SizeOfGuid]; Init(a, b, c, d); } private void Init(Int32 a, Int16 b, Int16 c, Byte[] d) { m_value[0] = (Byte)(a); m_value[1] = (Byte)(a >> 8); m_value[2] = (Byte)(a >> 16); m_value[3] = (Byte)(a >> 24); m_value[4] = (Byte)(b); m_value[5] = (Byte)(b >> 8); m_value[6] = (Byte)(c); m_value[7] = (Byte)(c >> 8); m_value[8] = d[0]; m_value[9] = d[1]; m_value[10] = d[2]; m_value[11] = d[3]; m_value[12] = d[4]; m_value[13] = d[5]; m_value[14] = d[6]; m_value[15] = d[7]; } /// <summary>使用指定的值初始化 CombGuid 結構的新實例。</summary> /// <param name="a">CombGuid 的開頭四個字節。</param> /// <param name="b">CombGuid 的下兩個字節。</param> /// <param name="c">CombGuid 的下兩個字節。</param> /// <param name="d">CombGuid 的下一個字節。</param> /// <param name="e">CombGuid 的下一個字節。</param> /// <param name="f">CombGuid 的下一個字節。</param> /// <param name="g">CombGuid 的下一個字節。</param> /// <param name="h">CombGuid 的下一個字節。</param> /// <param name="i">CombGuid 的下一個字節。</param> /// <param name="j">CombGuid 的下一個字節。</param> /// <param name="k">CombGuid 的下一個字節。</param> public CombGuid(Int32 a, Int16 b, Int16 c, Byte d, Byte e, Byte f, Byte g, Byte h, Byte i, Byte j, Byte k) { m_value = new Byte[_SizeOfGuid]; m_value[0] = (Byte)(a); m_value[1] = (Byte)(a >> 8); m_value[2] = (Byte)(a >> 16); m_value[3] = (Byte)(a >> 24); m_value[4] = (Byte)(b); m_value[5] = (Byte)(b >> 8); m_value[6] = (Byte)(c); m_value[7] = (Byte)(c >> 8); m_value[8] = d; m_value[9] = e; m_value[10] = f; m_value[11] = g; m_value[12] = h; m_value[13] = i; m_value[14] = j; m_value[15] = k; } #endregion #region -- 方法 -- #region - ToByteArray - /// <summary>將此 CombGuid 結構轉換為字節數組</summary> /// <returns>16 元素字節數組。</returns> public Byte[] ToByteArray() { var ret = new Byte[_SizeOfGuid]; m_value.CopyTo(ret, 0); return ret; } #endregion #region - GetByteArray - /// <summary>直接獲取此 CombGuid 結構內部的字節數組,如果此 CombGuid 為空,返回空值。 /// <para>調用此方法後,不要對獲取的字節數組做任何改變!!!</para> /// </summary> /// <returns>16 元素字節數組 或 null。</returns> [EditorBrowsable(EditorBrowsableState.Advanced)] public Byte[] GetByteArray() { return m_value; } #endregion #region - ToString - /// <summary>已重載,將此 CombGuid 結構轉換為字符串。</summary> /// <returns>返回該 CombGuid 結構的字符串表示形式。</returns> public override String ToString() { return ToString(CombGuidFormatStringType.Comb); } /// <summary>根據所提供的格式方式,返回此 CombGuid 實例值的字符串表示形式。</summary> /// <param name="formatType">格式化方式,它指示如何格式化此 CombGuid 的值。</param> /// <returns>此 CombGuid 的值,用一系列指定格式的小寫十六進制位表示</returns> public String ToString(CombGuidFormatStringType formatType) { if (IsNull) { return _NullString; } var offset = 0; var strLength = 36; var dash = true; if (formatType == CombGuidFormatStringType.Guid32Digits || formatType == CombGuidFormatStringType.Comb32Digits) { strLength = 32; dash = false; } var guidChars = new Char[strLength]; var isComb = formatType == CombGuidFormatStringType.Comb || formatType == CombGuidFormatStringType.Comb32Digits; #region MS GUID類內部代碼 //g[0] = (Byte)(_a); //g[1] = (Byte)(_a >> 8); //g[2] = (Byte)(_a >> 16); //g[3] = (Byte)(_a >> 24); //g[4] = (Byte)(_b); //g[5] = (Byte)(_b >> 8); //g[6] = (Byte)(_c); //g[7] = (Byte)(_c >> 8); //g[8] = _d; //g[9] = _e; //g[10] = _f; //g[11] = _g; //g[12] = _h; //g[13] = _i; //g[14] = _j; //g[15] = _k; //// [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] //offset = HexsToChars(guidChars, offset, _a >> 24, _a >> 16); //offset = HexsToChars(guidChars, offset, _a >> 8, _a); //if (dash) guidChars[offset++] = '-'; //offset = HexsToChars(guidChars, offset, _b >> 8, _b); //if (dash) guidChars[offset++] = '-'; //offset = HexsToChars(guidChars, offset, _c >> 8, _c); //if (dash) guidChars[offset++] = '-'; //offset = HexsToChars(guidChars, offset, _d, _e); //if (dash) guidChars[offset++] = '-'; //offset = HexsToChars(guidChars, offset, _f, _g); //offset = HexsToChars(guidChars, offset, _h, _i); //offset = HexsToChars(guidChars, offset, _j, _k); #endregion if (isComb) { offset = HexsToChars(guidChars, offset, m_value[10], m_value[11]); offset = HexsToChars(guidChars, offset, m_value[12], m_value[13]); offset = HexsToChars(guidChars, offset, m_value[14], m_value[15]); if (dash) { guidChars[offset++] = '-'; } offset = HexsToChars(guidChars, offset, m_value[8], m_value[9]); if (dash) { guidChars[offset++] = '-'; } offset = HexsToChars(guidChars, offset, m_value[6], m_value[7]); if (dash) { guidChars[offset++] = '-'; } offset = HexsToChars(guidChars, offset, m_value[4], m_value[5]); if (dash) { guidChars[offset++] = '-'; } offset = HexsToChars(guidChars, offset, m_value[0], m_value[1]); offset = HexsToChars(guidChars, offset, m_value[2], m_value[3]); } else { offset = HexsToChars(guidChars, offset, m_value[3], m_value[2]); offset = HexsToChars(guidChars, offset, m_value[1], m_value[0]); if (dash) { guidChars[offset++] = '-'; } offset = HexsToChars(guidChars, offset, m_value[5], m_value[4]); if (dash) { guidChars[offset++] = '-'; } offset = HexsToChars(guidChars, offset, m_value[7], m_value[6]); if (dash) { guidChars[offset++] = '-'; } offset = HexsToChars(guidChars, offset, m_value[8], m_value[9]); if (dash) { guidChars[offset++] = '-'; } offset = HexsToChars(guidChars, offset, m_value[10], m_value[11]); offset = HexsToChars(guidChars, offset, m_value[12], m_value[13]); offset = HexsToChars(guidChars, offset, m_value[14], m_value[15]); } return new String(guidChars, 0, strLength); } private static Int32 HexsToChars(Char[] guidChars, Int32 offset, Int32 a, Int32 b) { guidChars[offset++] = HexToChar(a >> 4); guidChars[offset++] = HexToChar(a); guidChars[offset++] = HexToChar(b >> 4); guidChars[offset++] = HexToChar(b); return offset; } private static Char HexToChar(Int32 a) { a = a & 0xf; return (Char)((a > 9) ? a - 10 + 0x61 : a + 0x30); } #endregion #endregion #region -- 靜態方法 -- private static readonly DateTime _CombBaseDate = new DateTime(1900, 1, 1); private static Int32 _LastDays; // 天數 private static Int32 _LastTenthMilliseconds; // 單位:1/10 毫秒 private static readonly Int32 _MaxTenthMilliseconds = 863999999; #region - NewComb - /// <summary>初始化 CombGuid 結構的新實例。</summary> /// <returns>一個新的 CombGuid 對象。</returns> public static CombGuid NewComb() { var guidArray = Guid.NewGuid().ToByteArray(); var now = DateTime.Now; // Get the days and milliseconds which will be used to build the Byte String var days = new TimeSpan(now.Ticks - _CombBaseDate.Ticks).Days; var tenthMilliseconds = (Int32)(now.TimeOfDay.TotalMilliseconds * 10D); var lastDays = _LastDays; var lastTenthMilliseconds = _LastTenthMilliseconds; if (days > lastDays) { Interlocked.CompareExchange(ref _LastDays, days, lastDays); Interlocked.CompareExchange(ref _LastTenthMilliseconds, tenthMilliseconds, lastTenthMilliseconds); } else { if (tenthMilliseconds > lastTenthMilliseconds) { Interlocked.CompareExchange(ref _LastTenthMilliseconds, tenthMilliseconds, lastTenthMilliseconds); } else { if (_LastTenthMilliseconds < Int32.MaxValue) { Interlocked.Increment(ref _LastTenthMilliseconds); } tenthMilliseconds = _LastTenthMilliseconds; } } // Convert to a Byte array var daysArray = BitConverter.GetBytes(days); var msecsArray = BitConverter.GetBytes(tenthMilliseconds); // Reverse the bytes to match SQL Servers ordering Array.Reverse(daysArray); Array.Reverse(msecsArray); // Copy the bytes into the guid Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2); //Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4); Array.Copy(msecsArray, 0, guidArray, guidArray.Length - 4, 4); return new CombGuid(guidArray, true); } #endregion #region - Parse - /// <summary>將 CombGuid 的字符串表示形式轉換為等效的 CombGuid 結構。</summary> /// <param name="s">包含下面任一格式的 CombGuid 的字符串(“d”表示忽略大小寫的十六進制數字): /// <para>32 個連續的數字 dddddddddddddddddddddddddddddddd </para> /// <para>- 或 - </para> /// <para>12 和 4、4、4、8 位數字的分組,各組之間有連線符,dddddddddddd-dddd-dddd-dddd-dddddddd</para> /// </param> /// <returns></returns> public static CombGuid Parse(String s) { if (_NullString == s) { return CombGuid.Null; } else { return new CombGuid(s); } } #endregion #region - TryParse - /// <summary>將 CombGuid 的字符串表示形式轉換為等效的 CombGuid 結構。</summary> /// <param name="comb">包含下面任一格式的 CombGuid 的字符串(“d”表示忽略大小寫的十六進制數字): /// <para>32 個連續的數字 dddddddddddddddddddddddddddddddd </para> /// <para>- 或 - </para> /// <para>12 和 4、4、4、8 位數字的分組,各組之間有連線符,dddddddddddd-dddd-dddd-dddd-dddddddd</para> /// </param> /// <param name="result">將包含已分析的值的結構。 如果此方法返回 true,result 包含有效的 CombGuid。 如果此方法返回 false,result 等於 CombGuid.Null。</param> /// <returns></returns> public static Boolean TryParse(String comb, out CombGuid result) { ValidationHelper.ArgumentNullOrEmpty(comb, "comb"); Int32 a; Int16 b, c; Byte[] d; if (new GuidParser(comb).TryParse(out a, out b, out c, out d)) { result = new CombGuid(a, b, c, d); return true; } result = Null; return false; } /// <summary>將 CombGuid 的字符串表示形式轉換為等效的 CombGuid 結構。</summary> /// <param name="value">Guid結構、CombGuid結構、16 元素字節數組 或 包含下面任一格式的 CombGuid 的字符串(“d”表示忽略大小寫的十六進制數字): /// <para>32 個連續的數字 dddddddddddddddddddddddddddddddd </para> /// <para>- 或 - </para> /// <para>12 和 4、4、4、8 位數字的分組,各組之間有連線符,dddddddddddd-dddd-dddd-dddd-dddddddd</para> /// </param> /// <param name="result">將包含已分析的值的結構。 如果此方法返回 true,result 包含有效的 CombGuid。 如果此方法返回 false,result 等於 CombGuid.Null。</param> /// <returns></returns> public static Boolean TryParse(Object value, out CombGuid result) { ValidationHelper.ArgumentNull(value, "comb"); var type = value.GetType(); if (type == typeof(String)) { return TryParse(value as String, out result); } else if (type == typeof(Byte[])) { var bs = value as Byte[]; if (bs.Length == _SizeOfGuid) { result = new CombGuid(bs); return true; } } else if (type == typeof(CombGuid)) { result = (CombGuid)value; return true; } else if (type == typeof(Guid)) { result = (CombGuid)value; return true; } result = Null; return false; } #endregion #endregion #region -- 類型轉換 -- /// <summary>將此 Guid 結構轉換為 CombGuid</summary> /// <param name="x">一個 Guid</param> /// <returns></returns> public static implicit operator CombGuid(Guid x) { return new CombGuid(x); } /// <summary>將此 CombGuid 結構轉換為 Guid</summary> /// <param name="x">一個 CombGuid</param> /// <returns></returns> public static explicit operator Guid(CombGuid x) { return x.Value; } #endregion #region -- 重載運算符 -- /// <summary>Comparison operators</summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> private static CombGuidComparison Compare(CombGuid x, CombGuid y) { // Swap to the correct order to be compared for (Int32 i = 0; i < _SizeOfGuid; i++) { Byte b1, b2; b1 = x.m_value[_GuidComparisonOrders[i]]; b2 = y.m_value[_GuidComparisonOrders[i]]; if (b1 != b2) { return (b1 < b2) ? CombGuidComparison.LT : CombGuidComparison.GT; } } return CombGuidComparison.EQ; } /// <summary>對兩個 CombGuid 結構執行邏輯比較,以確定它們是否相等</summary> /// <param name="x">一個 CombGuid 結構</param> /// <param name="y">一個 CombGuid 結構</param> /// <returns>它在兩個 CombGuid 結構相等時為 True,在兩個實例不等時為 False。</returns> public static Boolean operator ==(CombGuid x, CombGuid y) { if (x.IsNull || y.IsNull) { return (x.IsNull && y.IsNull); } else { return Compare(x, y) == CombGuidComparison.EQ; } } /// <summary>對兩個 CombGuid 結構執行邏輯比較,以確定它們是否不相等。</summary> /// <param name="x">一個 CombGuid 結構</param> /// <param name="y">一個 CombGuid 結構</param> /// <returns>它在兩個 CombGuid 結構不等時為 True,在兩個實例相等時為 False。</returns> public static Boolean operator !=(CombGuid x, CombGuid y) { return !(x == y); } /// <summary>對 CombGuid 結構的兩個實例進行比較,以確定第一個實例是否小於第二個實例。</summary> /// <param name="x">一個 CombGuid 結構</param> /// <param name="y">一個 CombGuid 結構</param> /// <returns>如果第一個實例小於第二個實例,則它為 True。 否則為 False。</returns> public static Boolean operator <(CombGuid x, CombGuid y) { if (x.IsNull || y.IsNull) { return (x.IsNull && !y.IsNull); } else { return Compare(x, y) == CombGuidComparison.LT; } } /// <summary>對 CombGuid 結構的兩個實例進行比較,以確定第一個實例是否大於第二個實例。</summary> /// <param name="x">一個 CombGuid 結構</param> /// <param name="y">一個 CombGuid 結構</param> /// <returns>如果第一個實例大於第二個實例,則它為 True。 否則為 False。</returns> public static Boolean operator >(CombGuid x, CombGuid y) { if (x.IsNull || y.IsNull) { return (!x.IsNull && y.IsNull); } else { return Compare(x, y) == CombGuidComparison.GT; } } /// <summary>對 CombGuid 結構的兩個實例進行比較,以確定第一個實例是否小於或等於第二個實例。</summary> /// <param name="x">一個 CombGuid 結構</param> /// <param name="y">一個 CombGuid 結構</param> /// <returns>如果第一個實例小於或等於第二個實例,則它為 True。 否則為 False。</returns> public static Boolean operator <=(CombGuid x, CombGuid y) { if (x.IsNull || y.IsNull) { return x.IsNull; } else { var cmp = Compare(x, y); return cmp == CombGuidComparison.LT || cmp == CombGuidComparison.EQ; } } /// <summary>對 CombGuid 結構的兩個實例進行比較,以確定第一個實例是否大於或等於第二個實例。</summary> /// <param name="x">一個 CombGuid 結構</param> /// <param name="y">一個 CombGuid 結構</param> /// <returns>如果第一個實例大於或等於第二個實例,則為 True。 否則為 False。</returns> public static Boolean operator >=(CombGuid x, CombGuid y) { if (x.IsNull || y.IsNull) { return y.IsNull; } else { var cmp = Compare(x, y); return cmp == CombGuidComparison.GT || cmp == CombGuidComparison.EQ; } } /// <summary>對兩個 CombGuid 結構執行邏輯比較,以確定它們是否相等</summary> /// <param name="x">一個 CombGuid 結構</param> /// <param name="y">一個 CombGuid 結構</param> /// <returns>它在兩個 CombGuid 結構相等時為 True,在兩個實例不等時為 False。</returns> public static Boolean Equals(CombGuid x, CombGuid y) { return (x == y); } /// <summary>對兩個 CombGuid 結構執行邏輯比較,以確定它們是否不相等。</summary> /// <param name="x">一個 CombGuid 結構</param> /// <param name="y">一個 CombGuid 結構</param> /// <returns>它在兩個 CombGuid 結構不等時為 True,在兩個實例相等時為 False。</returns> public static Boolean NotEquals(CombGuid x, CombGuid y) { return (x != y); } /// <summary>對 CombGuid 結構的兩個實例進行比較,以確定第一個實例是否小於第二個實例。</summary> /// <param name="x">一個 CombGuid 結構</param> /// <param name="y">一個 CombGuid 結構</param> /// <returns>如果第一個實例小於第二個實例,則它為 True。 否則為 False。</returns> public static Boolean LessThan(CombGuid x, CombGuid y) { return (x < y); } /// <summary>對 CombGuid 結構的兩個實例進行比較,以確定第一個實例是否大於第二個實例。</summary> /// <param name="x">一個 CombGuid 結構</param> /// <param name="y">一個 CombGuid 結構</param> /// <returns>如果第一個實例大於第二個實例,則它為 True。 否則為 False。</returns> public static Boolean GreaterThan(CombGuid x, CombGuid y) { return (x > y); } /// <summary>對 CombGuid 結構的兩個實例進行比較,以確定第一個實例是否小於或等於第二個實例。</summary> /// <param name="x">一個 CombGuid 結構</param> /// <param name="y">一個 CombGuid 結構</param> /// <returns>如果第一個實例小於或等於第二個實例,則它為 True。 否則為 False。</returns> public static Boolean LessThanOrEqual(CombGuid x, CombGuid y) { return (x <= y); } /// <summary>對 CombGuid 結構的兩個實例進行比較,以確定第一個實例是否大於或等於第二個實例。</summary> /// <param name="x">一個 CombGuid 結構</param> /// <param name="y">一個 CombGuid 結構</param> /// <returns>如果第一個實例大於或等於第二個實例,則為 True。 否則為 False。</returns> public static Boolean GreaterThanOrEqual(CombGuid x, CombGuid y) { return (x >= y); } #endregion #region -- CombGuid 相等 -- /// <summary>已重載,判斷兩個 CombGuid 結構是否相等</summary> /// <param name="value"></param> /// <returns></returns> public override Boolean Equals(Object value) { if ((value.GetType() != typeof(CombGuid))) { return false; } return this == (CombGuid)value; } /// <summary>判斷兩個 CombGuid 結構是否相等</summary> /// <param name="value"></param> /// <returns></returns> public Boolean Equals(CombGuid value) { return this == value; } /// <summary>已重載,獲取該 CombGuid 結構的哈希代碼</summary> /// <returns></returns> public override Int32 GetHashCode() { return IsNull ? 0 : Value.GetHashCode(); } #endregion #region -- INullable 成員 -- /// <summary>獲取一個布爾值,該值指示此 CombGuid 結構是否為 null。</summary> public Boolean IsNull { get { return (m_value == null); } } #endregion #region -- IComparable 成員 -- /// <summary>將此 CombGuid 結構與所提供的對象進行比較,並返回其相對值的指示。 不僅僅是比較最後 6 個字節,但會將最後 6 個字節視為比較中最重要的字節。</summary> /// <param name="value">要比較的對象</param> /// <returns>一個有符號的數字,它指示該實例和對象的相對值。 /// <para>小於零,此實例小於對象。</para> /// <para>零,此實例等於對象。</para> /// <para>大於零,此實例大於對象;或對象是 null 引用 (Nothing)</para> /// </returns> public Int32 CompareTo(Object value) { if (value.GetType() == typeof(CombGuid)) { var combGuid = (CombGuid)value; return CompareTo(combGuid); } throw new ArgumentException("value 類型不是 CombGuid"); } /// <summary>將此 CombGuid 結構與所提供的 CombGuid 結構進行比較,並返回其相對值的指示。 不僅僅是比較最後 6 個字節,但會將最後 6 個字節視為比較中最重要的字節。</summary> /// <param name="value">要比較的 CombGuid 結構</param> /// <returns>一個有符號的數字,它指示該實例和對象的相對值。 /// <para>小於零,此實例小於對象。</para> /// <para>零,此實例等於對象。</para> /// <para>大於零,此實例大於對象;或對象是 null 引用 (Nothing)</para> /// </returns> public Int32 CompareTo(CombGuid value) { // If both Null, consider them equal. // Otherwise, Null is less than anything. if (IsNull) { return value.IsNull ? 0 : -1; } else if (value.IsNull) { return 1; } //if (this < value) { return -1; } //if (this > value) { return 1; } //return 0; var cmp = Compare(this, value); switch (cmp) { case CombGuidComparison.LT: return -1; case CombGuidComparison.GT: return 1; case CombGuidComparison.EQ: default: return 0; } } #endregion #region -- IXmlSerializable 成員 -- XmlSchema IXmlSerializable.GetSchema() { return null; } /// <summary>從 CombGuid 結構的 XML 表示形式生成該對象</summary> /// <param name="reader"></param> void IXmlSerializable.ReadXml(XmlReader reader) { var isNull = reader.GetAttribute(_NullString, XmlSchema.InstanceNamespace); if (isNull != null && XmlConvert.ToBoolean(isNull)) { // VSTFDevDiv# 479603 - SqlTypes read null value infinitely and never read the next value. Fix - Read the next value. reader.ReadElementString(); m_value = null; } else { m_value = new Guid(reader.ReadElementString()).ToByteArray(); } } /// <summary>將該 CombGuid 結構轉換為其 XML 表示形式</summary> /// <param name="writer"></param> void IXmlSerializable.WriteXml(XmlWriter writer) { if (IsNull) { writer.WriteAttributeString("xsi", _NullString, XmlSchema.InstanceNamespace, "true"); } else { writer.WriteString(XmlConvert.ToString(new Guid(m_value))); } } public static XmlQualifiedName GetXsdType(XmlSchemaSet schemaSet) { return new XmlQualifiedName("String", XmlSchema.Namespace); } #endregion #region -- struct GuidParser -- private struct GuidParser { private String _src; private Int32 _length; private Int32 _cur; internal GuidParser(String src) { _src = src.Trim(); _cur = 0; _length = _src.Length; } private void Reset() { _cur = 0; _length = _src.Length; } private Boolean Eof { get { return _cur >= _length; } } internal Boolean TryParse(out Int32 a, out Int16 b, out Int16 c, out Byte[] d) { var hasHyphen = _length == 36; a = 0; b = 0; c = 0; d = null; UInt64 _a, _b, _c; if (!ParseHex(8, hasHyphen, out _a)) { return false; } if (hasHyphen && !ParseChar('-')) { return false; } if (!ParseHex(4, hasHyphen, out _b)) { return false; } if (hasHyphen && !ParseChar('-')) { return false; } if (!ParseHex(4, hasHyphen, out _c)) { return false; } if (hasHyphen && !ParseChar('-')) { return false; } var _d = new Byte[8]; for (Int32 i = 0; i < _d.Length; i++) { UInt64 dd; if (!ParseHex(2, hasHyphen, out dd)) { return false; } if (i == 1 && hasHyphen && !ParseChar('-')) { return false; } _d[i] = (Byte)dd; } if (!Eof) { return false; } a = (Int32)_a; b = (Int16)_b; c = (Int16)_c; d = _d; return true; } private Boolean ParseChar(Char c) { if (!Eof && _src[_GuidParseOrders36[_cur]] == c) { _cur++; return true; } return false; } private Boolean ParseHex(Int32 length, Boolean hasHyphen, out UInt64 res) //Boolean strict { res = 0; for (Int32 i = 0; i < length; i++) { //if (Eof) // return !(strict && (i + 1 != length)); if (Eof) { return !((i + 1 != length)); } var c = hasHyphen ? _src[_GuidParseOrders36[_cur]] : _src[_GuidParseOrders32[_cur]]; if (Char.IsDigit(c)) { res = res * 16 + c - '0'; _cur++; continue; } if (c >= 'a' && c <= 'f') { res = res * 16 + c - 'a' + 10; _cur++; continue; } if (c >= 'A' && c <= 'F') { res = res * 16 + c - 'A' + 10; _cur++; continue; } //if (!strict) // return true; return false; //!(strict && (i + 1 != length)); } return true; } } #endregion #region -- enum CombGuidComparison -- private enum CombGuidComparison { LT, EQ, GT } #endregion } /// <summary>CombGuid 結構格式化字符串方式</summary> public enum CombGuidFormatStringType { /// <summary>Guid 格式字符串,用一系列指定格式的小寫十六進制位表示,由連字符("-")分隔的 32 位數字,這些十六進制位分別以 8 個、4 個、4 個、4 個和 12 個位為一組並由連字符分隔開。</summary> Guid, /// <summary>Guid 格式字符串,用一系列指定格式的小寫十六進制位表示,32 位數字,這些十六進制位分別以 8 個、4 個、4 個、4 個和 12 個位為一組合並而成。</summary> Guid32Digits, /// <summary>CombGuid 格式字符串,用一系列指定格式的小寫十六進制位表示,由連字符("-")分隔的 32 位數字,這些十六進制位分別以 12 個和 4 個、4 個、4 個、8 個位為一組並由連字符分隔開。</summary> Comb, /// <summary>CombGuid 格式字符串,用一系列指定格式的小寫十六進制位表示,32 位數字,這些十六進制位分別以 12 個和 4 個、4 個、4 個、8 個位為一組合並而成。</summary> Comb32Digits } }
這個是經典問題了
是采用int型(自增量或手動增量),還是GUID還是聯合主鍵(combo)
考慮這些問題無非從高效性和易用性上進行考慮。下面列出四種主鍵生成方式優缺點的比較:
自動增長字段
優點1. 使用簡單
缺點1. 不同數據庫獲取當前值方式不同;
2. 難以應用在多個數據庫間進行數據遷移的情況。
3.不能集群化
手動增長型字段
優點1.可以獲得最新鍵值
2. 可以確保數據合並過程中不會出現鍵值沖突
缺點1.通常情況下需要建立一張單獨的表存儲當前主鍵鍵值;
2.增加一次數據庫訪問來獲取當前主鍵鍵值;
3. 考慮並發沖突等,增加系統的復雜程度。
4. 不能集群化
使用GUID
優點 1. 直接生成GUID,獲得最新鍵值以填充主鍵,使用方便;
2.可以確保數據合並過程中不會出現鍵值沖突;
3. 避免了前兩種方式獲取當前鍵值所增加的開銷。
缺點1.占用較多存儲空間;
2.索引耗時;
3. 在多表鏈接查詢時效率不如int型
使用“COMB”類型
優點1. 保留GUID的已有優點;
2. 利用時間信息與GUID組合起來,增加有序性以提高索引效率。
缺點1.需要設計COMB的生成算法;
2. 和GUID一樣占用較多存儲空間;
3. 在多表鏈接查詢時效率不如int型,但優於GUID。
從上表的對比中可以看出,問題的焦點還是在是采用高效的,但可控性、可移植性差的整形,還是采用能使用GUID這樣可控性和移植性高,但是效率低,存儲大的字符型主鍵,真有點魚和熊掌不能兼得的味道。(COMB需要設計生成算法,增加程序的復雜度,如果算法不當,會產生意想不到的結果,GUID也可以通過優化索引的方式提升性能,暫不使用COMB)
從數據庫的角度來看,整形雖然查詢的效率最高,但是數據的合並、移植存在著很大的問題,同時高並發的情況下,各種整形的生成方式都面臨這問題,而且不利於集群化處理。而采用GUID生成方式的字符型,能很好解決集成和並發性的問題,但占用空間大,查詢效率低可能成為系統運行後將出現的問題。
從程序開發的角度上看,整形生成方式的生成主鍵非常方便,但是主鍵的獲取,需要整個事務結束,才能從數據庫中取到,同時在多關聯表保存的時候,需要先保存主表,將產生的主鍵傳給字表,從而也可以造成性能的缺失,並且無法直接獲取主鍵,會增加程序開發處理的復雜性。而字符型的主鍵,需要程序人員自定義主鍵生成規則,需要認為的干預主鍵的生成,但是主鍵可以在插入數據庫之前就能拿到,方便程序的處理。
從系統數據的角度來看,業務數據可能存在大量的並發,采用GUID的方式是非常方便的,在數據級別很大的情況下,可以方便的進行集群化處理。檔案型數據並發量小,但是被引用的多,數據合並和集成的情況也很多,完全使用整形是不合適的,完全采用GUID,又會引起性能的缺失,需要更加折中的方案,既保證使用可控性較強的能唯一標識的字符串,同時又要盡量降低字符串占得字節數。而對於系統輔助數據,根據實際情況靈活使用,不做硬性統一,在數據量較小的情況下,盡量采用整形。