在分布式系統(tǒng)中,生成全局唯一的ID是一個(gè)核心問題。這不僅關(guān)系到數(shù)據(jù)的唯一性,還直接影響到系統(tǒng)的性能和擴(kuò)展性。本文將介紹幾種常見的分布式ID生成方式,并通過C#示例代碼展示其中一種實(shí)現(xiàn)方式——Snowflake算法。
分布式ID生成方式概述
1. UUID
UUID(Universally Unique Identifier)是一種通過一系列算法生成的128位數(shù)字,通?;跁r(shí)間戳、計(jì)算機(jī)硬件標(biāo)識(shí)符、隨機(jī)數(shù)等元素。UUID的優(yōu)點(diǎn)是實(shí)現(xiàn)簡單,無需網(wǎng)絡(luò)交互,保證了ID的全球唯一性。但其缺點(diǎn)也很明顯,如ID較長(36個(gè)字符的字符串形式),可能導(dǎo)致存儲(chǔ)和索引效率低下,且通常不能保證順序性。
2. 數(shù)據(jù)庫自增ID
基于數(shù)據(jù)庫的auto_increment
自增ID也可以生成分布式ID。其優(yōu)點(diǎn)是實(shí)現(xiàn)簡單,ID單調(diào)自增,數(shù)值類型查詢速度快。然而,在高并發(fā)場景下,單點(diǎn)數(shù)據(jù)庫會(huì)成為性能瓶頸,且存在單點(diǎn)故障風(fēng)險(xiǎn)。
3. Redis生成ID
Redis是一個(gè)高性能的鍵值數(shù)據(jù)庫,通過其原子性的INCR和INCRBY命令可以生成唯一的遞增數(shù)值。Redis的高性能保證了即使在高負(fù)載下也能快速生成ID,但高度依賴網(wǎng)絡(luò),且需要額外的基礎(chǔ)設(shè)施管理和維護(hù)。
4. 雪花算法(Snowflake)
雪花算法是Twitter開源的一種分布式ID生成算法,生成的是一個(gè)64位的長整型數(shù)字。該算法通過組合時(shí)間戳、數(shù)據(jù)中心ID、機(jī)器ID和序列號(hào)來確保ID的全局唯一性和趨勢遞增。Snowflake算法不依賴于數(shù)據(jù)庫,適合分布式環(huán)境,且生成ID的性能高。
Snowflake算法詳解及C#實(shí)現(xiàn)
Snowflake算法組成
Snowflake生成的ID由以下幾部分組成:
- 第1位:未使用,固定為0(由于Java中l(wèi)ong的最高位是符號(hào)位,正數(shù)是0,負(fù)數(shù)是1)。
- 時(shí)間戳(41位):記錄時(shí)間戳,毫秒級(jí)。支持69年的時(shí)間范圍。
- 數(shù)據(jù)中心ID(5位):用來區(qū)分不同的數(shù)據(jù)中心。
- 機(jī)器ID(5位):用來區(qū)分同一數(shù)據(jù)中心內(nèi)的不同機(jī)器。
- 序列號(hào)(12位):同一毫秒內(nèi)產(chǎn)生的不同ID的序列號(hào)。
C#實(shí)現(xiàn)Snowflake算法
下面是一個(gè)用C#實(shí)現(xiàn)的Snowflake算法示例:
using System;
public class SnowflakeIdWorker
{
private const long SEQUENCE_BIT = 12; // 序列號(hào)占用的位數(shù)
private const long MACHINE_BIT = 5; // 機(jī)器標(biāo)識(shí)占用的位數(shù)
private const long DATACENTER_BIT = 5; // 數(shù)據(jù)中心占用的位數(shù)
private const long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
private const long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
private const long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
private long datacenterId; // 數(shù)據(jù)中心ID
private long machineId; // 機(jī)器ID
private long sequence = 0L; // 序列號(hào)
private long lastStmp = -1L; // 上一次時(shí)間戳
// 起始的時(shí)間戳
private const long START_STMP = 1480166465631L;
public SnowflakeIdWorker(long datacenterId, long machineId)
{
if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0)
{
throw new ArgumentException("數(shù)據(jù)中心ID超出范圍");
}
if (machineId > MAX_MACHINE_NUM || machineId < 0)
{
throw new ArgumentException("機(jī)器ID超出范圍");
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
public long NextId()
{
long currStmp = GetNewstmp();
if (currStmp < lastStmp)
{
throw new Exception("時(shí)鐘倒退, ID生成失敗");
}
if (currStmp == lastStmp)
{
// 相同毫秒內(nèi),序列號(hào)自增
sequence = (sequence + 1) & MAX_SEQUENCE;
if (sequence == 0L)
{
currStmp = GetNextMill();
}
}
else
{
// 不同毫秒內(nèi),序列號(hào)置為0
sequence = 0L;
}
lastStmp = currStmp;
return ((currStmp - START_STMP) << (SEQUENCE_BIT + MACHINE_BIT + DATACENTER_BIT)) |
(datacenterId << (SEQUENCE_BIT + MACHINE_BIT)) |
(machineId << SEQUENCE_BIT) |
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;
}
}
class Program
{
static void Main(string[] args)
{
SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 1);
Console.WriteLine(idWorker.NextId());
}
}
代碼解釋
上述C#代碼實(shí)現(xiàn)了一個(gè)SnowflakeIdWorker類,該類通過構(gòu)造函數(shù)接收數(shù)據(jù)中心ID和機(jī)器ID,并通過NextId方法生成下一個(gè)全局唯一的ID。代碼通過位運(yùn)算將時(shí)間戳、數(shù)據(jù)中心ID、機(jī)器ID和序列號(hào)組合成一個(gè)64位的長整型數(shù)字,確保ID的全局唯一性和趨勢遞增。
結(jié)論
分布式ID生成方式多種多樣,每種方式都有其特定的應(yīng)用場景和優(yōu)缺點(diǎn)。Snowflake算法作為一種高效、可靠的分布式ID生成方式,被廣泛應(yīng)用于各種分布式系統(tǒng)中。通過C#實(shí)現(xiàn)Snowflake算法,可以方便地集成到各種分布式應(yīng)用中,滿足全局唯一ID的生成需求。
該文章在 2024/7/8 12:43:20 編輯過