C#學習筆記
環境 |
Visual Studio 2015 C# |
目的: |
SnowFlake演算法 C#的實作,可以應用於高併發下產生具64 bit 自增式唯一碼的流水號 |
應用: | DB Insert每筆資料時,產生的流水號 |
Github: | |
本篇分為四部分: | |
一、 | SnowFlake原理 |
二、 | C# 程式碼架構 |
三、 | 實作程式碼說明 |
四、 | 參考文獻 |
一、 SnowFlake原理
Step 1:以下是SnowFlake原理圖 使用了63bit 其中
Step 2: 關鍵點:每次為了取得唯一值,需要搭配 TimeStamp (微秒) + MacAddress (自增序號為高併發時,最大可容納數)
12 bit 允許範圍: 0~4095
二、C#程式碼架構
Step 1:主程式
Step 2: SnowFlake主程式 : SnowFlakeAlg
SnowFlake產生ID的Mathod: SnowFlakeAlg.Method
三、實作程式碼說明
Step 1:Form.cs 主程式-執行100次
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine(SnowFlakeAlg.GetGuid());
}
}
Step 2: SnowFlakeAlg.cs
主程式: 用一個Lock 確保產生的Id 是遞增的,如果在相同微秒下,已經用完了4095個序號,則會強制換下一微秒
/// <summary>
/// 分布式自增演算法 - 產生唯一ID
/// </summary>
public static partial class SnowFlakeAlg
{
/// <summary>
/// 鎖住的資源對象
/// </summary>
private static object lockItem = new object();
/// <summary>
/// 上一個時間戳- 進行比較用
/// </summary>
private static long lastDateTimeStamp = 0L;
/// <summary>
/// Sequence 序列號 允許0~4095
/// </summary>
private static long sequence = 0L;
public static long GetGuid()
{
//非同步情況下,使用Lock 確保產生唯一ID
lock(lockItem)
{
long result = 0L;
//1. 41bit
long timelong = SnowFlakeAlg.GetTimeSpan.GetTimeSpanReturnLong();
//2. 10bit
long macSn = SnowFlakeAlg.GetMacAddress.GetTenBitMacAddress();
//3. 12bit
sequence++;
//seq == 0 表示 同一秒內已經被排了4095次,Seq 已經用盡,切換至下一秒
if (timelong == lastDateTimeStamp)
{
if (true == SnowFlakeAlg.GetSequence.checkSeq(sequence))
{
//取得下一微秒的Long值
timelong = SnowFlakeAlg.GetTimeSpan.GetTimeSpanReturnNextSecondLong();
}
}
else//不同微秒下
{
sequence = 0;//歸0
}
//紀錄本次的TimeStamp
lastDateTimeStamp = timelong;
//41bit
result =((timelong) << 22) | macSn << 12 | sequence;
return result;
}
}
}
Step 3: SnowFlakeAlg.Method
以下共三種Class組合,分別:
1. GetMacAddress : 取得Mac 位址的方法 , 並且取得 XX-XX-XX-XX-XX-XX 取得後面兩個Byte (Mac 必定為6Byte 組成 ,我們需要10bit 可自由從裡面挑出兩個Byte)
2. GetTimeSpan: 取得微秒轉成Long 的大小,這是核心,唯有時間才會符合 自增 + 不重複 這個條件
3. GetSequence: 判斷是否同一微秒下,4095個號碼是否用盡。
#region Method
#region Mac Function 10bit
private static class GetMacAddress
{
/// <summary>
/// Ascii對應表
/// </summary>
private static Dictionary<char, int> dicAscii = new Dictionary<char, int>()
{
{'0' , 0},{ '1' ,1} , {'2' , 2},{ '3' ,3} , {'4' , 4},{ '5' ,5} , {'6' , 6},{ '7' ,7} , {'8' , 8},{ '9' ,9} , {'A' , 10},{ 'B' ,11} , {'C' , 12},{ 'D' ,13} , { 'E' ,14} ,{'F' , 15}
};
/// <summary>
/// 取得10位元的網路卡位址
/// </summary>
/// <returns></returns>
public static long GetTenBitMacAddress()
{
//取得網卡Libary - 取得本機器所有網路卡
NetworkInterface[] macs = NetworkInterface.GetAllNetworkInterfaces();
//取得電腦上的 Ethernet 的 MAC Address ,第一個抓到的實體網卡
var result = macs.Where(o => o.NetworkInterfaceType == NetworkInterfaceType.Ethernet).FirstOrDefault();
//沒有網卡
if (null == result)
{
return 0;
} //return 0L;
else//有網卡則進行計算
{
//※邏輯 : SnowFlake 演算法取10bit ,實體網卡為 12Byte EX: E0-3F-49-4D-01-1C
// 取最後兩個Byte(2 * 8) 進行6bit位移,取10Bit
//String -> ASCII
byte[] macDecByte = System.Text.Encoding.ASCII.GetBytes(result.GetPhysicalAddress().ToString());
//左邊
int left = AscIIToInt((char)Convert.ToInt32(macDecByte[8])) * 16 + AscIIToInt((char)Convert.ToInt32(macDecByte[9])) * 1 << 8;//=>x的位元 x x x x x x x x o o o o o o o o
int right = AscIIToInt((char)Convert.ToInt32(macDecByte[10])) * 16 + AscIIToInt((char)Convert.ToInt32(macDecByte[11])) * 1;//=> x的位元 o o o o o o o o x x x x x x x x
int total = left + right;//相加
//保留 10 bit => (最大整數4095 如右邊x的部分)=> o o o o o o x x x x x x x x x x
total = total >> 6;//
return total;
}
}
/// <summary>
/// 將AscII碼 轉為 Int
/// </summary>
/// <returns></returns>
private static int AscIIToInt(char item)
{
int resultValue = 0;
//取得對應 Char -> Value
dicAscii.TryGetValue(item, out resultValue);
//返回16進位數值
return resultValue;
}
}
#endregion
#region TimeSpan Milliseconds 41bit
/// <summary>
/// 取得時間戳
/// </summary>
private static class GetTimeSpan
{
/// <summary>
/// 回傳當前時間微秒 Long型態
/// </summary>
/// <returns></returns>
public static long GetTimeSpanReturnLong()
{
DateTime dt = DateTime.Now;//現在時間
DateTime ori = new DateTime(1970, 1, 1, 0, 0, 0);//起源時間
return (long)(dt - ori).TotalMilliseconds;
}
/// <summary>
/// 回傳當前時間微秒 + 1 Long 型態
/// </summary>
/// <returns></returns>
public static long GetTimeSpanReturnNextSecondLong()
{
DateTime dt = DateTime.Now.AddMilliseconds(1);//增加1微秒
DateTime ori = new DateTime(1970, 1, 1, 0, 0, 0);//起源時間
return (long)(dt - ori).TotalMilliseconds;
}
}
#endregion
#region Sequence 12bit
/// <summary>
/// 取得序列號
/// </summary>
private static class GetSequence
{
/// <summary>
/// 12bit 最大長度
/// </summary>
private static long BIT12 = 4095;
public static bool checkSeq(long nowSeq)
{
var check= (nowSeq) & BIT12;
if (check == 0)
return true;
else
return false;
}
}
#endregion
#endregion
Step 4: 執行結果
四、參考文獻
Twitter Blog:https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake.html