數據庫主鍵的選擇--雪花ID


在設計表結構時,我們首先遇到的問題就是主鍵設置為什么類型的。之前我用過int 也用過GUID,都不太理想:

使用int主鍵缺點

    1、如果經常有合並表操作,就可能會出現主鍵重復情況。

    2、使用int 數據范圍有限制。如果存在大量數據,可能會超出int 取值范圍。

    3、很難處理分布式存儲數據表。

使用GUID做主鍵缺點:

    1、存儲空間大(16 byte),因此它將會占用更多磁盤大小。

    2、很難記憶。join操作性能比int要低。

    3、沒有內置函數獲取最新產生guid主鍵

    4、GUID做主鍵將會添加到表上所以其他索引中,因此會降低性能。

    5、不宜排序。

這次我選擇了使用雪花ID。雪花ID是用一個64位的整形數字來做ID,對應.net中的long,數據庫中的bigint。

 

 

算法描述:

    • 最高位是符號位,始終為0,不可用。
    • 41位的時間序列,精確到毫秒級,41位的長度可以使用69年。時間位還有一個很重要的作用是可以根據時間進行排序。
    • 10位的機器標識,10位的長度最多支持部署1024個節點。
    • 12位的計數序列號,序列號即一系列的自增id,可以支持同一節點同一毫秒生成多個ID序號,12位的計數序列號支持每個節點每毫秒產生4096個ID序號。

源碼如下:

 1 public class SnowflakeIdWorker//雪花ID
 2     {
 3         //機器ID
 4         private static long workerId;
 5         private static long twepoch = 687888001020L; //唯一時間,這是一個避免重復的隨機量,自行設定不要大於當前時間戳
 6         private static long sequence = 0L;
 7         private static int workerIdBits = 4; //機器碼字節數。4個字節用來保存機器碼(定義為Long類型會出現,最大偏移64位,所以左移64位沒有意義)
 8         public static long maxWorkerId = -1L ^ -1L << workerIdBits; //最大機器ID
 9         private static int sequenceBits = 10; //計數器字節數,10個字節用來保存計數碼
10         private static int workerIdShift = sequenceBits; //機器碼數據左移位數,就是后面計數器占用的位數
11         private static int timestampLeftShift = sequenceBits + workerIdBits; //時間戳左移動位數就是機器碼和計數器總字節數
12         public static long sequenceMask = -1L ^ -1L << sequenceBits; //一微秒內可以產生計數,如果達到該值則等到下一微妙在進行生成
13         private long lastTimestamp = -1L;
14 
15         /// <summary>
16         /// 機器碼
17         /// </summary>
18         /// <param name="workerId"></param>
19         public SnowflakeIdWorker()
20         {
21             long workerId = 1;
22             if (workerId > maxWorkerId || workerId < 0)
23                 throw new Exception(string.Format("worker Id can't be greater than {0} or less than 0 ", workerId));
24             SnowflakeIdWorker.workerId = workerId;
25         }
26 
27         public long NextId()
28         {
29             lock (this)
30             {
31                 long timestamp = timeGen();
32                 if (this.lastTimestamp == timestamp)
33                 { //同一微妙中生成ID
34                     SnowflakeIdWorker.sequence = (SnowflakeIdWorker.sequence + 1) & SnowflakeIdWorker.sequenceMask; //用&運算計算該微秒內產生的計數是否已經到達上限
35                     if (SnowflakeIdWorker.sequence == 0)
36                     {
37                         //一微妙內產生的ID計數已達上限,等待下一微妙
38                         timestamp = tillNextMillis(this.lastTimestamp);
39                     }
40                 }
41                 else
42                 { //不同微秒生成ID
43                     SnowflakeIdWorker.sequence = 0; //計數清0
44                 }
45                 if (timestamp < lastTimestamp)
46                 { //如果當前時間戳比上一次生成ID時時間戳還小,拋出異常,因為不能保證現在生成的ID之前沒有生成過
47                     throw new Exception(string.Format("Clock moved backwards.  Refusing to generate id for {0} milliseconds",
48                         this.lastTimestamp - timestamp));
49                 }
50                 this.lastTimestamp = timestamp; //把當前時間戳保存為最后生成ID的時間戳
51                 long nextId = (timestamp - twepoch << timestampLeftShift) | SnowflakeIdWorker.workerId << SnowflakeIdWorker.workerIdShift | SnowflakeIdWorker.sequence;
52                 return nextId;
53             }
54         }
55 
56         /// <summary>
57         /// 獲取下一微秒時間戳
58         /// </summary>
59         /// <param name="lastTimestamp"></param>
60         /// <returns></returns>
61         private long tillNextMillis(long lastTimestamp)
62         {
63             long timestamp = timeGen();
64             while (timestamp <= lastTimestamp)
65             {
66                 timestamp = timeGen();
67             }
68             return timestamp;
69         }
70 
71         /// <summary>
72         /// 生成當前時間戳
73         /// </summary>
74         /// <returns></returns>
75         private long timeGen()
76         {
77             return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
78         }
79     }

 

調用時:

SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker();

ID= snowflakeIdWorker.NextId().ToString();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM