C#生成唯一值的方法汇总

生成唯一值的方法很多,下面就不同环境下生成的唯一标识方法一一介绍,作为工作中的一次总结,有兴趣的可以自行测试:

一、在 .NET 中生成

1、直接用.NET Framework 提供的 Guid() 函数,此种方法使用非常广泛。GUID(全局统一标识符)是指在一台机器上生成的数字,它保证对在同一时空中的任何两台计算机都不会生成重复的 GUID 值(即保证所有机器都是唯一的)。关于GUID的介绍在此不作具体熬述,想深入了解可以自行查阅MSDN。代码如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 namespace ConsoleApplication1
 6 {
 7     class Program
 8     {
 9         static void Main(string[] args)
10         {
11             string _guid = GetGuid();
12             Console.WriteLine("唯一码:{0}\t长度为:{1}\n去掉连接符:{2}", _guid, _guid.Length, _guid.Replace("-", ""));
13             string uniqueIdString = GuidTo16String();
14             Console.WriteLine("唯一码:{0}\t长度为:{1}", uniqueIdString, uniqueIdString.Length);
15             long uniqueIdLong = GuidToLongID();
16             Console.WriteLine("唯一码:{0}\t长度为:{1}", uniqueIdLong, uniqueIdLong.ToString().Length);
17         }
18         /// <summary>
19         /// 由连字符分隔的32位数字
20         /// </summary>
21         /// <returns></returns>
22         private static string GetGuid()
23         {
24             System.Guid guid = new Guid();
25             guid = Guid.NewGuid();
26             return guid.ToString();
27         }
28         /// <summary>  
29         /// 根据GUID获取16位的唯一字符串  
30         /// </summary>  
31         /// <param name=\"guid\"></param>  
32         /// <returns></returns>  
33         public static string GuidTo16String()
34         {
35             long i = 1;
36             foreach (byte b in Guid.NewGuid().ToByteArray())
37                 i *= ((int)b + 1);
38             return string.Format("{0:x}", i - DateTime.Now.Ticks);
39         }
40         /// <summary>  
41         /// 根据GUID获取19位的唯一数字序列  
42         /// </summary>  
43         /// <returns></returns>  
44         public static long GuidToLongID()
45         {
46             byte[] buffer = Guid.NewGuid().ToByteArray();
47             return BitConverter.ToInt64(buffer, 0);
48         }   
49     }
50 }

2、用 DateTime.Now.ToString("yyyyMMddHHmmssms") 和 .NET Framework 提供的 RNGCryptoServiceProvider() 结合生成,代码如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6 namespace ConsoleApplication1
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             string uniqueNum = GenerateOrderNumber();
13             Console.WriteLine("唯一码:{0}\t 长度为:{1}", uniqueNum, uniqueNum.Length);
14             //测试是否会生成重复
15               Console.WriteLine("时间+RNGCryptoServiceProvider()结合生成的唯一值,如下:");
16             string _tempNum = string.Empty;
17             for (int i = 0; i < 1000; i++)
18             {
19                 string uNum = GenerateOrderNumber();
20                 Console.WriteLine(uNum);
21                 if (string.Equals(uNum, _tempNum))
22                 {
23                     Console.WriteLine("上值存在重复,按Enter键继续");
24                     Console.ReadKey();
25                 }
26                 //Sleep当前线程,是为了延时,从而不产生重复值。可以把它注释掉测试看
27                 Thread.Sleep(300);
28                 _tempNum = uNum;
29             }
30         }
31         /// <summary>
32         /// 唯一订单号生成
33         /// </summary>
34         /// <returns></returns>
35         public static string GenerateOrderNumber()
36         {
37             string strDateTimeNumber = DateTime.Now.ToString("yyyyMMddHHmmssms");
38             string strRandomResult = NextRandom(1000, 1).ToString();
39             return strDateTimeNumber + strRandomResult;
40         }
41         /// <summary>
42         /// 参考:msdn上的RNGCryptoServiceProvider例子
43         /// </summary>
44         /// <param name="numSeeds"></param>
45         /// <param name="length"></param>
46         /// <returns></returns>
47         private static int NextRandom(int numSeeds, int length)
48         {
49             // Create a byte array to hold the random value.  
50             byte[] randomNumber = new byte[length];
51             // Create a new instance of the RNGCryptoServiceProvider.  
52             System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
53             // Fill the array with a random value.  
54             rng.GetBytes(randomNumber);
55             // Convert the byte to an uint value to make the modulus operation easier.  
56             uint randomResult = 0x0;
57             for (int i = 0; i < length; i++)
58             {
59                 randomResult |= ((uint)randomNumber[i] << ((length - 1 - i) * 8));
60             }
61             return (int)(randomResult % numSeeds) + 1;
62         }
63     }
64 }

3、用 [0-9A-Z] + Guid.NewGuid() 结合生成特定位数的唯一字符串,代码如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 namespace ConsoleApplication1
 6 { 
 7     class Program
 8     {
 9         static void Main(string[] args)
10         {
11             string uniqueText = GenerateUniqueText(8);
12             Console.WriteLine("唯一码:{0}\t 长度为:{1}", uniqueText, uniqueText.Length);
13             //测试是否会生成重复 
14               Console.WriteLine("由[0-9A-Z] + NewGuid() 结合生成的唯一值,如下:");
15             IList<string> list = new List<string>();
16             for (int i = 1; i <= 1000; i++)
17             {
18                 string _uT = GenerateUniqueText(8);
19                 Console.WriteLine("{0}\t{1}", list.Count, _uT);
20                 if (list.Contains(_uT))
21                 {
22                     Console.WriteLine("{0}值存在重复", _uT);
23                     Console.ReadKey();
24                 }
25                 list.Add(_uT);
26                 //if (i % 200 == 0)
27                 //{
28                     //Console.WriteLine("没有重复,按Enter键往下看");
29                     //Console.ReadKey();
30                 //}
31             }
32             list.Clear();
33         }
34 
35         /// <summary>
36         /// 生成特定位数的唯一字符串
37         /// </summary>
38         /// <param name="num">特定位数</param>
39         /// <returns></returns>
40         public static string GenerateUniqueText(int num)
41         {
42             string randomResult = string.Empty;
43             string readyStr = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
44             char[] rtn = new char[num];
45             Guid gid = Guid.NewGuid();
46             var ba = gid.ToByteArray();
47             for (var i = 0; i < num; i++)
48             {
49                 rtn[i] = readyStr[((ba[i] + ba[num + i]) % 35)];
50             }
51             foreach (char r in rtn)
52             {
53                 randomResult += r;
54             }
55             return randomResult;
56         }
57     }
58 }

4、用单例模式实现,由[0-9a-z]组合生成的唯一值,此文不讨论单例模式的多种实现方式与性能问题,随便弄一种方式实现,代码如下:

Demo结构如图: 

Program.cs 程序:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Collections;
  6 using System.Xml;
  7 namespace ConsoleApplication4
  8 {
  9     class Program
 10     {
 11         static void Main(string[] args)
 12         {
 13             CreateID createID = CreateID.GetInstance();
 14             //测试是否会生成重复 
 15             Console.WriteLine("单例模式实现,由[0-9a-z]组合生成的唯一值,如下:");
 16             IList<string> list = new List<string>();
 17             for (int i = 1; i <= 1000000000; i++)
 18             {
 19                 string strUniqueNum = createID.CreateUniqueID();
 20                 Console.WriteLine("{0}\t{1}", list.Count, strUniqueNum);
 21                 if (list.Contains(strUniqueNum))
 22                 {
 23                     Console.WriteLine("{0}值存在重复", strUniqueNum);
 24                     Console.ReadKey();
 25                 }
 26                 list.Add(strUniqueNum);
 27                 if (i % 200 == 0)
 28                 {
 29                     Console.WriteLine("没有重复,按Enter键往下看");
 30                     Console.ReadKey();
 31                 }
 32             }
 33             list.Clear();
 34         }
 35     }
 36     /// <summary>
 37     /// 单例模式实现
 38     /// 唯一值由[0-9a-z]组合而成,且生成的每个ID不能重复
 39     /// </summary>
 40     public class CreateID
 41     {
 42         private static CreateID _instance;
 43         private static readonly object syncRoot = new object();
 44         private EHashtable hashtable = new EHashtable();
 45         private string _strXMLURL = string.Empty;
 46         private CreateID()
 47         {
 48             hashtable.Add("0", "0");
 49             hashtable.Add("1", "1");
 50             hashtable.Add("2", "2");
 51             hashtable.Add("3", "3");
 52             hashtable.Add("4", "4");
 53             hashtable.Add("5", "5");
 54             hashtable.Add("6", "6");
 55             hashtable.Add("7", "7");
 56             hashtable.Add("8", "8");
 57             hashtable.Add("9", "9");
 58             hashtable.Add("10", "a");
 59             hashtable.Add("11", "b");
 60             hashtable.Add("12", "c");
 61             hashtable.Add("13", "d");
 62             hashtable.Add("14", "e");
 63             hashtable.Add("15", "f");
 64             hashtable.Add("16", "g");
 65             hashtable.Add("17", "h");
 66             hashtable.Add("18", "i");
 67             hashtable.Add("19", "j");
 68             hashtable.Add("20", "k");
 69             hashtable.Add("21", "l");
 70             hashtable.Add("22", "m");
 71             hashtable.Add("23", "n");
 72             hashtable.Add("24", "o");
 73             hashtable.Add("25", "p");
 74             hashtable.Add("26", "q");
 75             hashtable.Add("27", "r");
 76             hashtable.Add("28", "s");
 77             hashtable.Add("29", "t");
 78             hashtable.Add("30", "u");
 79             hashtable.Add("31", "v");
 80             hashtable.Add("32", "w");
 81             hashtable.Add("33", "x");
 82             hashtable.Add("34", "y");
 83             hashtable.Add("35", "z");
 84             _strXMLURL = System.IO.Path.GetFullPath(@"..\..\") + "XMLs\\record.xml";
 85 
 86         }
 87         public static CreateID GetInstance()
 88         {
 89             if (_instance == null)
 90             {
 91                 lock (syncRoot)
 92                 {
 93                     if (_instance == null)
 94                     {
 95                         _instance = new CreateID();
 96                     }
 97                 }
 98             }
 99             return _instance;
100         }
101         /// <summary>
102         /// 创建UniqueID
103         /// </summary>
104         /// <returns>UniqueID</returns>
105         public string CreateUniqueID()
106         {
107             long _uniqueid = GetGuidFromXml();
108             return Convert10To36(_uniqueid);
109         }
110         /// <summary>
111         /// 获取UniqueID总记录,即获取得到的这个ID是第几个ID
112         /// 更新UniqueID使用的个数,用于下次使用
113         /// </summary>
114         /// <returns></returns>
115         private long GetGuidFromXml()
116         {
117             long record = 0;
118             XmlDocument xmldoc = new XmlDocument();
119             xmldoc.Load(_strXMLURL);
120             XmlElement rootNode = xmldoc.DocumentElement;
121             //此次的个数值
122             record = Convert.ToInt64(rootNode["record"].InnerText);
123             //此次的个数值+1 == 下次的个数值
124             rootNode["record"].InnerText = Convert.ToString(record + 1);
125             xmldoc.Save(_strXMLURL);
126             return record;
127         }
128         /// <summary>
129         /// 10进制转36进制
130         /// </summary>
131         /// <param name="intNum10">10进制数</param>
132         /// <returns></returns>
133         private string Convert10To36(long intNum10)
134         {
135             string strNum36 = string.Empty;
136             long result = intNum10 / 36;
137             long remain = intNum10 % 36;
138             if (hashtable.ContainsKey(remain.ToString()))
139                 strNum36 = hashtable[remain.ToString()].ToString() + strNum36;
140             intNum10 = result;
141             while (intNum10 / 36 != 0)
142             {
143                 result = intNum10 / 36;
144                 remain = intNum10 % 36;
145                 if (hashtable.ContainsKey(remain.ToString()))
146                     strNum36 = hashtable[remain.ToString()].ToString() + strNum36;
147                 intNum10 = result;
148             }
149             if (intNum10 > 0 && intNum10 < 36)
150             {
151                 if (hashtable.ContainsKey(intNum10.ToString()))
152                     strNum36 = hashtable[intNum10.ToString()].ToString() + strNum36;
153             }
154             return strNum36;
155         }
156     }
157     /// <summary>
158     /// Summary description for EHashTable
159     /// </summary>
160     public class EHashtable : Hashtable
161     {
162         private ArrayList list = new ArrayList();
163         public override void Add(object key, object value)
164         {
165             base.Add(key, value);
166             list.Add(key);
167         }
168         public override void Clear()
169         {
170             base.Clear();
171             list.Clear();
172         }
173         public override void Remove(object key)
174         {
175             base.Remove(key);
176             list.Remove(key);
177         }
178         public override ICollection Keys
179         {
180             get
181             {
182                 return list;
183             }
184         }
185     }
186 }

XML:

1 <?xml version="1.0" encoding="utf-8"?>
2 <root>
3   <record id="record">1</record>
4 </root>

二、在JS中生成GUID,类似.NET中的 Guid.NewGuid(),代码如下:

 1 function newGuid() { //方法一:
 2     var guid = "";
 3     var n = (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
 4     for (var i = 1; i <= 8; i++) {
 5         guid += n;
 6     }
 7     return guid;
 8 }
 9 function newGuid() { //方法二:
10     var guid = "";
11     for (var i = 1; i <= 32; i++) {
12         var n = Math.floor(Math.random() * 16.0).toString(16);
13         guid += n;
14         if ((i == 8) || (i == 12) || (i == 16) || (i == 20))
15             guid += "-";
16     }
17     return guid;
18 }

 

三、在SQL存储过程生成GUID,代码如下:

 1 -- =============================================
 2 -- Author:      JBen
 3 -- Create date: 2012-06-05
 4 -- Description: 生成唯一标识ID,公共存储过程,可设置在别的存储过程调用此存储过程传不同的前缀
 5 -- =============================================
 6 ALTER PROCEDURE [dbo].[pro_CreateGuid] 
 7     @Prefix NVARCHAR(10),
 8     @outputV_guid NVARCHAR(40) OUTPUT
 9 AS
10 BEGIN
11     -- SET NOCOUNT ON added to prevent extra result sets from
12     -- interfering with SELECT statements.
13     SET NOCOUNT ON;
14     -- Insert statements for procedure here
15     SET @outputV_guid = @Prefix + REPLACE(CAST(NEWID() AS VARCHAR(36)),'-','')
16 END

 

5. Twitter的snowflake算法

snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看https://github.com/twitter/snowflake

C#代码如下:

/// <summary>
    /// From: https://github.com/twitter/snowflake
    /// An object that generates IDs.
    /// This is broken into a separate class in case
    /// we ever want to support multiple worker threads
    /// per process
    /// </summary>
    public class IdWorker
    {
        private long workerId;
        private long datacenterId;
        private long sequence = 0L;

        private static long twepoch = 1288834974657L;

        private static long workerIdBits = 5L;
        private static long datacenterIdBits = 5L;
        private static long maxWorkerId = -1L ^ (-1L << (int)workerIdBits);
        private static long maxDatacenterId = -1L ^ (-1L << (int)datacenterIdBits);
        private static long sequenceBits = 12L;

        private long workerIdShift = sequenceBits;
        private long datacenterIdShift = sequenceBits + workerIdBits;
        private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
        private long sequenceMask = -1L ^ (-1L << (int)sequenceBits);

        private long lastTimestamp = -1L;
        private static object syncRoot = new object();

        public IdWorker(long workerId, long datacenterId)
        {

            // sanity check for workerId
            if (workerId > maxWorkerId || workerId < 0)
            {
                throw new ArgumentException(string.Format("worker Id can't be greater than %d or less than 0", maxWorkerId));
            }
            if (datacenterId > maxDatacenterId || datacenterId < 0)
            {
                throw new ArgumentException(string.Format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }

        public long nextId()
        {
            lock (syncRoot)
            {
                long timestamp = timeGen();

                if (timestamp < lastTimestamp)
                {
                    throw new ApplicationException(string.Format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
                }

                if (lastTimestamp == timestamp)
                {
                    sequence = (sequence + 1) & sequenceMask;
                    if (sequence == 0)
                    {
                        timestamp = tilNextMillis(lastTimestamp);
                    }
                }
                else
                {
                    sequence = 0L;
                }

                lastTimestamp = timestamp;

                return ((timestamp - twepoch) << (int)timestampLeftShift) | (datacenterId << (int)datacenterIdShift) | (workerId << (int)workerIdShift) | sequence;
            }
        }

        protected long tilNextMillis(long lastTimestamp)
        {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp)
            {
                timestamp = timeGen();
            }
            return timestamp;
        }

        protected long timeGen()
        {
            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
        }
    }

 

测试代码如下:

private static void TestIdWorker()
        {
            HashSet<long> set = new HashSet<long>();
            IdWorker idWorker1 = new IdWorker(0, 0);
            IdWorker idWorker2 = new IdWorker(1, 0);
            Thread t1 = new Thread(() => DoTestIdWoker(idWorker1, set));
            Thread t2 = new Thread(() => DoTestIdWoker(idWorker2, set));
            t1.IsBackground = true;
            t2.IsBackground = true;

            t1.Start();
            t2.Start();
            try
            {
                Thread.Sleep(30000);
                t1.Abort();
                t2.Abort();
            }
            catch (Exception e)
            {
            }

            Console.WriteLine("done");
        }

        private static void DoTestIdWoker(IdWorker idWorker, HashSet<long> set)
        {
            while (true)
            {
                long id = idWorker.nextId();
                if (!set.Add(id))
                {
                    Console.WriteLine("duplicate:" + id);
                }

                Thread.Sleep(1);
            }
        }

 

 

snowflake算法可以根据自身项目的需要进行一定的修改。比如估算未来的数据中心个数,每个数据中心的机器数以及统一毫秒可以能的并发数来调整在算法中所需要的bit数。

优点:

1)不依赖于数据库,灵活方便,且性能优于数据库。

2)ID按照时间在单机上是递增的。

缺点:

1)在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,也许有时候也会出现不是全局递增的情况。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值