前言
在我的印象中用到这个雪花id比较少,可能是我接触的大型项目或者开源项目比较少,同时接触到中大型分布式也比较少,基本都是自研系统,用的是自增id和guidvalue作为唯一编号。
最近项目上使用了一套第三方框架代码,使用了雪花id作为表的唯一主键,并且之前表没有这个字段,需要进行表迁移的同时初始化雪花id字段值。
因此,趁这次机会简单总结下雪花id以及在sql server上如何生成雪花id。
认识雪花id
雪花id是twitter开发的一种分布式唯一id生成算法,主要用于在分布式系统中生成全局唯一的id标识符。它的名称来源于"自然界中没有两片完全相同的雪花"这一概念,象征着每个生成的id都是独一无二的。
雪花id的核心特点
- 全局唯一性:在分布式系统中生成的id不会重复
- 时间有序性:id按照时间顺序递增
- 高性能:本地生成,不依赖数据库等外部系统
- 可解析:id中包含的信息可以被解析出来
雪花id的结构(64位)
标准的雪花id由以下三部分组成(共64位):
| 1位符号位 | 41位时间戳 | 10位工作节点id | 12位序列号 |
具体分解:
- 符号位(1位):始终为0,保证id为正数
- 时间戳(41位):毫秒级的时间戳,可以使用约69年
- 通常从自定义纪元开始计算(如twitter使用2010-11-04 01:42:54 utc)
- 工作节点id(10位):
- 通常分为5位数据中心id + 5位机器id
- 最多支持32个数据中心,每个数据中心32台机器
- 序列号(12位):同一毫秒内的序列号,支持每毫秒生成4096个id
雪花id的优势
- 分布式友好:不同节点可以独立生成id而不需要协调
- 时间有序:生成的id按时间递增,有利于数据库索引
- 高性能:本地生成,不依赖网络或数据库
- 信息丰富:id本身包含时间、节点等信息
雪花id的局限性
- 时钟依赖:严重依赖系统时钟,时钟回拨会导致id重复
- 节点id配置:需要手动或通过外部系统分配节点id
- 时间耗尽:41位时间戳大约69年后会耗尽
雪花id的应用场景
- 分布式系统主键生成
- 订单号、交易号等业务编号
- 日志跟踪id
- 任何需要全局唯一且有序id的场景
示例id解析
假设一个雪花id:123456789012345678
转换为二进制后可以解析出:
- 时间戳部分:可以转换为具体的生成时间
- 工作节点部分:知道是在哪个数据中心哪台机器生成的
- 序列号部分:知道这是该毫秒内生成的第几个id
雪花id因其简单高效的特性,已经成为分布式系统id生成的经典解决方案之一。
生成雪花id
雪花id是twitter提出的一种分布式id生成算法,它生成64位的唯一id,通常包含时间戳、工作节点id和序列号。
在sql server中可以通过以下几种方式实现雪花id的生成:
使用t-sql函数实现
-- 创建配置表
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和数据中心id需要在每个节点上配置不同) insert into snowflakeconfig (machineid, datacenterid, lasttimestamp, sequence) values (1, 1, 0, 0);
-- 创建获取当前时间戳的函数
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;
-- 创建等待下一毫秒的函数
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
-- 创建计算幂的函数(替代位移操作)
create function poweroftwo(@exponent bigint)
returns bigint
as
begin
return cast(power(cast(2 as float), @exponent) as bigint);
end;
go
-- 创建生成雪花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;
-- 验证参数
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;
-- 获取当前状态
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
-- 同一毫秒内生成多个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
-- 更新状态
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;

到此这篇关于sqlserver中生成雪花id(snowflake id)的实现方法的文章就介绍到这了,更多相关sqlserver生成雪花id内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论