当前位置: 代码网 > it编程>数据库>MsSqlserver > SQLServer中生成雪花ID(Snowflake ID)的实现方法

SQLServer中生成雪花ID(Snowflake ID)的实现方法

2025年08月08日 MsSqlserver 我要评论
前言在我的印象中用到这个雪花id比较少,可能是我接触的大型项目或者开源项目比较少,同时接触到中大型分布式也比较少,基本都是自研系统,用的是自增id和guidvalue作为唯一编号。最近项目上使用了一套

前言

在我的印象中用到这个雪花id比较少,可能是我接触的大型项目或者开源项目比较少,同时接触到中大型分布式也比较少,基本都是自研系统,用的是自增id和guidvalue作为唯一编号。
最近项目上使用了一套第三方框架代码,使用了雪花id作为表的唯一主键,并且之前表没有这个字段,需要进行表迁移的同时初始化雪花id字段值。
因此,趁这次机会简单总结下雪花id以及在sql server上如何生成雪花id。

认识雪花id

雪花id是twitter开发的一种分布式唯一id生成算法,主要用于在分布式系统中生成全局唯一的id标识符。它的名称来源于"自然界中没有两片完全相同的雪花"这一概念,象征着每个生成的id都是独一无二的。

雪花id的核心特点

  1. 全局唯一性:在分布式系统中生成的id不会重复
  2. 时间有序性:id按照时间顺序递增
  3. 高性能:本地生成,不依赖数据库等外部系统
  4. 可解析:id中包含的信息可以被解析出来

雪花id的结构(64位)

标准的雪花id由以下三部分组成(共64位):

| 1位符号位 | 41位时间戳 | 10位工作节点id | 12位序列号 |

具体分解:

  1. 符号位(1位):始终为0,保证id为正数
  2. 时间戳(41位):毫秒级的时间戳,可以使用约69年
    • 通常从自定义纪元开始计算(如twitter使用2010-11-04 01:42:54 utc)
  3. 工作节点id(10位)
    • 通常分为5位数据中心id + 5位机器id
    • 最多支持32个数据中心,每个数据中心32台机器
  4. 序列号(12位):同一毫秒内的序列号,支持每毫秒生成4096个id

雪花id的优势

  1. 分布式友好:不同节点可以独立生成id而不需要协调
  2. 时间有序:生成的id按时间递增,有利于数据库索引
  3. 高性能:本地生成,不依赖网络或数据库
  4. 信息丰富:id本身包含时间、节点等信息

雪花id的局限性

  1. 时钟依赖:严重依赖系统时钟,时钟回拨会导致id重复
  2. 节点id配置:需要手动或通过外部系统分配节点id
  3. 时间耗尽:41位时间戳大约69年后会耗尽

雪花id的应用场景

  1. 分布式系统主键生成
  2. 订单号、交易号等业务编号
  3. 日志跟踪id
  4. 任何需要全局唯一且有序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内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com