SQLServer中生成雪花ID(Snowflake?ID)的實現(xiàn)方法
前言
在我的印象中用到這個雪花ID比較少,可能是我接觸的大型項目或者開源項目比較少,同時接觸到中大型分布式也比較少,基本都是自研系統(tǒng),用的是自增ID和GuidValue作為唯一編號。
最近項目上使用了一套第三方框架代碼,使用了雪花ID作為表的唯一主鍵,并且之前表沒有這個字段,需要進行表遷移的同時初始化雪花ID字段值。
因此,趁這次機會簡單總結(jié)下雪花ID以及在Sql Server上如何生成雪花ID。
認識雪花ID
雪花ID是Twitter開發(fā)的一種分布式唯一ID生成算法,主要用于在分布式系統(tǒng)中生成全局唯一的ID標識符。它的名稱來源于"自然界中沒有兩片完全相同的雪花"這一概念,象征著每個生成的ID都是獨一無二的。
雪花ID的核心特點
- 全局唯一性:在分布式系統(tǒng)中生成的ID不會重復
- 時間有序性:ID按照時間順序遞增
- 高性能:本地生成,不依賴數(shù)據(jù)庫等外部系統(tǒng)
- 可解析:ID中包含的信息可以被解析出來
雪花ID的結(jié)構(gòu)(64位)
標準的雪花ID由以下三部分組成(共64位):
| 1位符號位 | 41位時間戳 | 10位工作節(jié)點ID | 12位序列號 |
具體分解:
- 符號位(1位):始終為0,保證ID為正數(shù)
- 時間戳(41位):毫秒級的時間戳,可以使用約69年
- 通常從自定義紀元開始計算(如Twitter使用2010-11-04 01:42:54 UTC)
- 工作節(jié)點ID(10位):
- 通常分為5位數(shù)據(jù)中心ID + 5位機器ID
- 最多支持32個數(shù)據(jù)中心,每個數(shù)據(jù)中心32臺機器
- 序列號(12位):同一毫秒內(nèi)的序列號,支持每毫秒生成4096個ID
雪花ID的優(yōu)勢
- 分布式友好:不同節(jié)點可以獨立生成ID而不需要協(xié)調(diào)
- 時間有序:生成的ID按時間遞增,有利于數(shù)據(jù)庫索引
- 高性能:本地生成,不依賴網(wǎng)絡或數(shù)據(jù)庫
- 信息豐富:ID本身包含時間、節(jié)點等信息
雪花ID的局限性
- 時鐘依賴:嚴重依賴系統(tǒng)時鐘,時鐘回撥會導致ID重復
- 節(jié)點ID配置:需要手動或通過外部系統(tǒng)分配節(jié)點ID
- 時間耗盡:41位時間戳大約69年后會耗盡
雪花ID的應用場景
- 分布式系統(tǒng)主鍵生成
- 訂單號、交易號等業(yè)務編號
- 日志跟蹤ID
- 任何需要全局唯一且有序ID的場景
示例ID解析
假設(shè)一個雪花ID:123456789012345678
轉(zhuǎn)換為二進制后可以解析出:
- 時間戳部分:可以轉(zhuǎn)換為具體的生成時間
- 工作節(jié)點部分:知道是在哪個數(shù)據(jù)中心哪臺機器生成的
- 序列號部分:知道這是該毫秒內(nèi)生成的第幾個ID
雪花ID因其簡單高效的特性,已經(jīng)成為分布式系統(tǒng)ID生成的經(jīng)典解決方案之一。
生成雪花ID
雪花ID是Twitter提出的一種分布式ID生成算法,它生成64位的唯一ID,通常包含時間戳、工作節(jié)點ID和序列號。
在SQL Server中可以通過以下幾種方式實現(xiàn)雪花ID的生成:
使用T-SQL函數(shù)實現(xiàn)
-- 創(chuàng)建配置表
CREATE TABLE SnowflakeConfig (
MachineId BIGINT NOT NULL,
DatacenterId BIGINT NOT NULL,
LastTimestamp BIGINT NOT NULL,
Sequence BIGINT NOT NULL,
CONSTRAINT PK_SnowflakeConfig PRIMARY KEY (MachineId, DatacenterId)
);
-- 初始化配置 (機器ID和數(shù)據(jù)中心ID需要在每個節(jié)點上配置不同) INSERT INTO SnowflakeConfig (MachineId, DatacenterId, LastTimestamp, Sequence) VALUES (1, 1, 0, 0);
-- 創(chuàng)建獲取當前時間戳的函數(shù)
CREATE FUNCTION GetCurrentTimestamp()
RETURNS BIGINT
AS
BEGIN
DECLARE @epoch DATETIME2 = '1970-01-01 00:00:00';
DECLARE @now DATETIME2 = SYSUTCDATETIME();
RETURN CAST(DATEDIFF_BIG(MILLISECOND, @epoch, @now) AS BIGINT);
END;
-- 創(chuàng)建等待下一毫秒的函數(shù)
CREATE FUNCTION TilNextMillis(@lastTimestamp BIGINT)
RETURNS BIGINT
AS
BEGIN
DECLARE @timestamp BIGINT;
SET @timestamp = dbo.GetCurrentTimestamp();
WHILE @timestamp <= @lastTimestamp
BEGIN
SET @timestamp = dbo.GetCurrentTimestamp();
END
RETURN @timestamp;
END;
GO
-- 創(chuàng)建計算冪的函數(shù)(替代位移操作)
CREATE FUNCTION PowerOfTwo(@exponent BIGINT)
RETURNS BIGINT
AS
BEGIN
RETURN CAST(POWER(CAST(2 AS FLOAT), @exponent) AS BIGINT);
END;
GO
-- 創(chuàng)建生成雪花ID的存儲過程
CREATE PROCEDURE GenerateSnowflakeId
@MachineId BIGINT = 1,
@DatacenterId BIGINT = 1,
@SnowflakeId BIGINT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
-- 常量定義
DECLARE @Twepoch BIGINT = 1700058600000;
DECLARE @MachineIdBits BIGINT = 5;
DECLARE @DatacenterIdBits BIGINT = 5;
DECLARE @SequenceBits BIGINT = 12;
-- 使用POWER計算替代位移
DECLARE @MaxMachineId BIGINT = dbo.PowerOfTwo(@MachineIdBits) - 1;
DECLARE @MaxDatacenterId BIGINT = dbo.PowerOfTwo(@DatacenterIdBits) - 1;
DECLARE @SequenceMask BIGINT = dbo.PowerOfTwo(@SequenceBits) - 1;
DECLARE @MachineIdShift BIGINT = @SequenceBits;
DECLARE @DatacenterIdShift BIGINT = @SequenceBits + @MachineIdBits;
DECLARE @TimestampLeftShift BIGINT = @SequenceBits + @MachineIdBits + @DatacenterIdBits;
-- 驗證參數(shù)
IF @MachineId > @MaxMachineId OR @MachineId < 0
BEGIN
THROW 50000, 'MachineId can''t be greater than maxMachineId or less than 0', 1;
RETURN;
END
IF @DatacenterId > @MaxDatacenterId OR @DatacenterId < 0
BEGIN
THROW 50000, 'DatacenterId can''t be greater than maxDatacenterId or less than 0', 1;
RETURN;
END
-- 使用事務確保原子性
BEGIN TRANSACTION;
BEGIN TRY
DECLARE @LastTimestamp BIGINT;
DECLARE @Sequence BIGINT;
DECLARE @Timestamp BIGINT;
-- 獲取當前狀態(tài)
SELECT @LastTimestamp = LastTimestamp, @Sequence = Sequence
FROM SnowflakeConfig WITH (UPDLOCK)
WHERE MachineId = @MachineId AND DatacenterId = @DatacenterId;
-- 獲取當前時間戳
SET @Timestamp = dbo.GetCurrentTimestamp();
-- 檢查時鐘回撥
IF @Timestamp < @LastTimestamp
BEGIN
ROLLBACK TRANSACTION;
THROW 50000, 'Clock moved backwards. Refusing to generate id', 1;
RETURN;
END
-- 同一毫秒內(nèi)生成多個ID
IF @LastTimestamp = @Timestamp
BEGIN
SET @Sequence = (@Sequence + 1) & @SequenceMask;
IF @Sequence = 0
BEGIN
-- 序列耗盡,等待下一毫秒
SET @Timestamp = dbo.TilNextMillis(@LastTimestamp);
END
END
ELSE
BEGIN
SET @Sequence = 0;
END
-- 更新狀態(tài)
UPDATE SnowflakeConfig
SET LastTimestamp = @Timestamp,
Sequence = @Sequence
WHERE MachineId = @MachineId AND DatacenterId = @DatacenterId;
-- 生成ID (使用乘法替代位移)
SET @SnowflakeId =
(@Timestamp - @Twepoch) * dbo.PowerOfTwo(@TimestampLeftShift) +
@DatacenterId * dbo.PowerOfTwo(@DatacenterIdShift) +
@MachineId * dbo.PowerOfTwo(@MachineIdShift) +
@Sequence;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
THROW;
END CATCH
END;
GO
查看效果
-- 使用存儲過程版本 DECLARE @Id BIGINT; EXEC GenerateSnowflakeId @MachineId = 1, @DatacenterId = 1, @SnowflakeId = @Id OUTPUT; SELECT @Id AS SnowflakeId;

到此這篇關(guān)于SQLServer中生成雪花ID(Snowflake ID)的實現(xiàn)方法的文章就介紹到這了,更多相關(guān)sqlserver生成雪花id內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SqlServer生成連續(xù)數(shù)字根據(jù)指定的數(shù)字操作
這篇文章主要介紹了SqlServer生成連續(xù)數(shù)字根據(jù)指定的數(shù)字操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10
SQLServer:探討EXEC與sp_executesql的區(qū)別詳解
本篇文章是對EXEC與sp_executesql的區(qū)別進行了詳細的分析介紹,需要的朋友參考下2013-06-06
SQL?Server數(shù)據(jù)庫備份和恢復數(shù)據(jù)庫的全過程
最近在功能調(diào)試前需要先將測試數(shù)據(jù)庫備份,然后功能調(diào)試之后再將測試數(shù)據(jù)庫還原,這樣就可以重復的進行功能調(diào)試,這篇文章主要給大家介紹了關(guān)于SQL?Server數(shù)據(jù)庫備份和恢復數(shù)據(jù)庫的相關(guān)資料,需要的朋友可以參考下2022-06-06
SQL?Server數(shù)據(jù)庫用戶管理及權(quán)限管理詳解
在SQLServer數(shù)據(jù)庫中,為了確保數(shù)據(jù)的安全性和完整性,我們需要為用戶分配適當?shù)臋?quán)限,這篇文章主要給大家介紹了關(guān)于SQL?Server數(shù)據(jù)庫用戶管理及權(quán)限管理的相關(guān)資料,需要的朋友可以參考下2024-07-07
必須會的SQL語句(八) 數(shù)據(jù)庫的完整性約束
這篇文章主要介紹了sqlserver中數(shù)據(jù)庫的完整性約束使用方法,需要的朋友可以參考下2015-01-01
sql server中批量插入與更新兩種解決方案分享(存儲過程)
對于sql 來說操作集合類型(一行一行)是比較麻煩的一件事,而一般業(yè)務邏輯復雜的系統(tǒng)或項目都會涉及到集合遍歷的問題,通常一些人就想到用游標,這里我列出了兩種方案,供大家參考2012-05-05

