1. 概述
分布式系統(tǒng)中,有一些需要使用全局唯一ID
的場景,這種時候?yàn)榱朔乐?code>ID沖突可以使用36位的UUID
,但是UUID
有一些缺點(diǎn),首先他相對比較長,另外UUID
一般是無序的。有些時候我們希望能使用一種簡單一些的ID
,并且希望ID
能夠按照時間有序生成。而Twitter
的snowflake
解決了這種需求,最初Twitter
把存儲系統(tǒng)從MySQL
遷移到Cassandra
,因?yàn)?code>Cassandra沒有順序ID
生成機(jī)制,所以開發(fā)了這樣一套全局唯一ID
生成服務(wù)。
該項(xiàng)目地址為:https://github.com/twitter/snowflake
是用 Scala
實(shí)現(xiàn)的
參考:
-
C# 分布式自增ID算法snowflake(雪花算法) - 五維思考 - 博客園 (cnblogs.com)
-
C#雪花Id_c# 雪花id-CSDN博客
2. 結(jié)構(gòu)
第1位 | 第2位 | 第3位 | 第4位 | 第5位 |
---|---|---|---|---|
位數(shù) | 時間戳(ms) | 數(shù)據(jù)中心ID(DatacenterId ) | 工作節(jié)點(diǎn)ID (MachineId ) | 自增序列號 |
0 | 0000000000 | 0000000000 | 0000000000 | 000000000000 |
- 第1位:未使用
- 第2位:接下來的41位為毫秒級時間(41位的長度可以使用69年),用毫秒級的時間戳來表示自1970年1月1日 00:00:00 GMT以來的時間。
- 第3-4位:用來區(qū)分不同的數(shù)據(jù)中心
datacenterId
和machineId
,可根據(jù)實(shí)際情況分配,最多可容納1024個數(shù)據(jù)中心(2^10=10位的長度最多支持部署1024個節(jié)點(diǎn)),也可以設(shè)置成5位,最大節(jié)點(diǎn)是32個。 - 最后12位是毫秒內(nèi)的計(jì)數(shù)(12位的計(jì)數(shù)順序號支持每個節(jié)點(diǎn)每毫秒產(chǎn)生4096個
ID
序號)
一共加起來剛好64位,為一個Long
型。(轉(zhuǎn)換成字符串長度為18)
snowflake
生成的ID
整體上按照時間自增排序,并且 整個分布式
系統(tǒng)內(nèi)不會產(chǎn)生ID
碰撞(由datacenter
和machineId
作區(qū)分),并且效率較高。據(jù)說:snowflake
每秒能夠產(chǎn)生26萬個ID
。
注意:
- 在實(shí)際使用中,需要根據(jù)不同的分布式環(huán)境配置合適的數(shù)據(jù)中心ID和工作節(jié)點(diǎn)ID,以保證生成的雪花Id的唯一性和順序性。
- 其中
dataCenterId
和workerId
分別是數(shù)據(jù)中心和工作節(jié)點(diǎn)的標(biāo)識,該生成器依賴于數(shù)據(jù)中心ID和工作節(jié)點(diǎn)ID兩個參數(shù)進(jìn)行初始化。具體的生成過程是根據(jù)當(dāng)前時間戳、數(shù)據(jù)中心ID、工作節(jié)點(diǎn)ID和自增序列號,通過位運(yùn)算組合生成一個64位的唯一標(biāo)識。
3. 代碼
3.1 IdWorker.cs
using System;
/// <summary>
/// Twitter的分布式自增ID雪花算法
/// </summary>
public class IdWorker
{
//起始的時間戳
private static long START_STMP = 1480166465631L;
//每一部分占用的位數(shù)
private static int SEQUENCE_BIT = 12; //序列號占用的位數(shù)
private static int MACHINE_BIT = 5; //機(jī)器標(biāo)識占用的位數(shù)
private static int DATACENTER_BIT = 5;//數(shù)據(jù)中心占用的位數(shù)
//每一部分的最大值
private static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
private static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
private static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
//每一部分向左的位移
private static int MACHINE_LEFT = SEQUENCE_BIT;
private static int DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private static int TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
private long datacenterId = 1; //數(shù)據(jù)中心
private long machineId = 1; //機(jī)器標(biāo)識
private long sequence = 0L; //序列號
private long lastStmp = -1L;//上一次時間戳
#region 單例:完全懶漢
private static readonly Lazy<IdWorker> lazy = new Lazy<IdWorker>(() => new IdWorker());
public static IdWorker Singleton { get { return lazy.Value; } }
private IdWorker() { }
#endregion
public IdWorker(long cid, long mid)
{
if (cid > MAX_DATACENTER_NUM || cid < 0) throw new Exception($"中心Id應(yīng)在(0,{MAX_DATACENTER_NUM})之間");
if (mid > MAX_MACHINE_NUM || mid < 0) throw new Exception($"機(jī)器Id應(yīng)在(0,{MAX_MACHINE_NUM})之間");
datacenterId = cid;
machineId = mid;
}
/// <summary>
/// 產(chǎn)生下一個ID
/// </summary>
/// <returns></returns>
public long nextId()
{
long currStmp = getNewstmp();
if (currStmp < lastStmp) throw new Exception("時鐘倒退,Id生成失敗!");
if (currStmp == lastStmp)
{
//相同毫秒內(nèi),序列號自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列數(shù)已經(jīng)達(dá)到最大
if (sequence == 0L) currStmp = getNextMill();
}
else
{
//不同毫秒內(nèi),序列號置為0
sequence = 0L;
}
lastStmp = currStmp;
return (currStmp - START_STMP) << TIMESTMP_LEFT //時間戳部分
| datacenterId << DATACENTER_LEFT //數(shù)據(jù)中心部分
| machineId << MACHINE_LEFT //機(jī)器標(biāo)識部分
| sequence; //序列號部分
}
private long getNextMill()
{
long mill = getNewstmp();
while (mill <= lastStmp)
{
mill = getNewstmp();
}
return mill;
}
private long getNewstmp()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
}
}
3.2 IdWorkerTest.cs (測試)
使用
IdWorker idworker = IdWorker.Singleton;
Console.WriteLine(idworker.nextId());
測試文章來源:http://www.zghlxwxcb.cn/news/detail-846552.html
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Test.Simple
{
public static class IdWorkerTest
{
public static void Test()
{
/***
*
* 兩種測試方法,均為500并發(fā),生成5000個Id:
* Machine1() 模擬1臺主機(jī),單例模式獲取實(shí)例
* Machine5() 模擬5臺主機(jī),創(chuàng)建5個實(shí)例
*/
Machine1();
Machine2();
Machine5();
}
public static void Machine1()
{
int cid = 1;
int mid = 15;
Console.WriteLine("雪花ID -- IdWorkerTest -- 模擬1臺主機(jī)( 數(shù)據(jù)中心{0} - 機(jī)器節(jié)點(diǎn){1}): ", cid, mid);
IdWorker idworker = new IdWorker(cid, mid);
Console.WriteLine(idworker.nextId());
cid = 2;
mid = 10;
Console.WriteLine("雪花ID -- IdWorkerTest -- 模擬1臺主機(jī)( 數(shù)據(jù)中心{0} - 機(jī)器節(jié)點(diǎn){1}): ", cid, mid);
idworker = new IdWorker(cid, mid);
Console.WriteLine(idworker.nextId());
}
public static void Machine2()
{
Console.WriteLine("雪花ID -- IdWorkerTest -- 模擬1臺主機(jī) : ");
for (int j = 0; j < 500; j++)
{
Task.Run(() =>
{
IdWorker idworker = IdWorker.Singleton;
for (int i = 0; i < 10; i++)
{
Console.WriteLine(idworker.nextId());
}
});
}
}
public static void Machine5()
{
Console.WriteLine("雪花ID -- IdWorkerTest -- 模擬5臺主機(jī) : ");
List<IdWorker> workers = new List<IdWorker>();
Random random = new Random();
for (int i = 0; i < 5; i++)
{
workers.Add(new IdWorker(1, i + 1));
}
for (int j = 0; j < 500; j++)
{
Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
int mid = random.Next(0, 5);
Console.WriteLine(workers[mid].nextId());
}
});
}
}
}
}
在這里插入圖片描述
結(jié)束文章來源地址http://www.zghlxwxcb.cn/news/detail-846552.html
到了這里,關(guān)于C# 分布式自增ID算法snowflake(雪花算法)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!