【Sql Server】在SQL Server中生成雪花ID(Snowflake ID)

大家好,我是全栈小5,欢迎来到《小5讲堂》。
这是《Sql Server》系列文章,每篇文章将以博主理解的角度展开讲解。
温馨提示:博主能力有限,理解水平有限,若有不对之处望指正!

在这里插入图片描述

目录

  • 前言
  • 认识雪花ID
    • 雪花ID的核心特点
    • 雪花ID的结构(64位)
    • 雪花ID的优势
    • 雪花ID的局限性
    • 雪花ID的应用场景
    • 示例ID解析
  • 生成雪花ID
    • 使用T-SQL函数实现
    • 查看效果
  • 文章推荐

前言

在我的印象中用到这个雪花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
BEGINDECLARE @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
BEGINDECLARE @timestamp BIGINT;SET @timestamp = dbo.GetCurrentTimestamp();WHILE @timestamp <= @lastTimestampBEGINSET @timestamp = dbo.GetCurrentTimestamp();ENDRETURN @timestamp;
END;
GO
-- 创建计算幂的函数(替代位移操作)
CREATE FUNCTION PowerOfTwo(@exponent BIGINT)
RETURNS BIGINT
AS
BEGINRETURN 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
BEGINSET 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 < 0BEGINTHROW 50000, 'MachineId can''t be greater than maxMachineId or less than 0', 1;RETURN;ENDIF @DatacenterId > @MaxDatacenterId OR @DatacenterId < 0BEGINTHROW 50000, 'DatacenterId can''t be greater than maxDatacenterId or less than 0', 1;RETURN;END-- 使用事务确保原子性BEGIN TRANSACTION;BEGIN TRYDECLARE @LastTimestamp BIGINT;DECLARE @Sequence BIGINT;DECLARE @Timestamp BIGINT;-- 获取当前状态SELECT @LastTimestamp = LastTimestamp, @Sequence = SequenceFROM SnowflakeConfig WITH (UPDLOCK)WHERE MachineId = @MachineId AND DatacenterId = @DatacenterId;-- 获取当前时间戳SET @Timestamp = dbo.GetCurrentTimestamp();-- 检查时钟回拨IF @Timestamp < @LastTimestampBEGINROLLBACK TRANSACTION;THROW 50000, 'Clock moved backwards. Refusing to generate id', 1;RETURN;END-- 同一毫秒内生成多个IDIF @LastTimestamp = @TimestampBEGINSET @Sequence = (@Sequence + 1) & @SequenceMask;IF @Sequence = 0BEGIN-- 序列耗尽,等待下一毫秒SET @Timestamp = dbo.TilNextMillis(@LastTimestamp);ENDENDELSEBEGINSET @Sequence = 0;END-- 更新状态UPDATE SnowflakeConfigSET LastTimestamp = @Timestamp,Sequence = @SequenceWHERE 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 TRYBEGIN CATCHROLLBACK TRANSACTION;THROW;END CATCH
END;
GO

查看效果

-- 使用存储过程版本
DECLARE @Id BIGINT;
EXEC GenerateSnowflakeId @MachineId = 1, @DatacenterId = 1, @SnowflakeId = @Id OUTPUT;
SELECT @Id AS SnowflakeId;

在这里插入图片描述

文章推荐

【Sql Server】使用row_number over方式进行表分页,数据量达到五千多条记录后,查询变慢需要20多秒的解决方案

【Sql Server】随机查询一条表记录,并重重温回顾下自定义函数的封装和使用

【Sql Server】锁表如何解锁,模拟会话事务方式锁定一个表然后进行解锁

【Sql Server】通过Sql语句批量处理数据,使用变量且遍历数据进行逻辑处理

【新星计划回顾】第六篇学习计划-通过自定义函数和存储过程模拟MD5数据

【新星计划回顾】第四篇学习计划-自定义函数、存储过程、随机值知识点

【Sql Server】Update中的From语句,以及常见更新操作方式

【Sql server】假设有三个字段a,b,c 以a和b分组,如何查询a和b唯一,但是c不同的记录

【Sql Server】新手一分钟看懂在已有表基础上修改字段默认值和数据类型

总结:温故而知新,不同阶段重温知识点,会有不一样的认识和理解,博主将巩固一遍知识点,并以实践方式和大家分享,若能有所帮助和收获,这将是博主最大的创作动力和荣幸。也期待认识更多优秀新老博主。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/74802.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML 表单处理进阶:验证与提交机制的学习心得与进度(一)

引言 在前端开发的广袤领域中&#xff0c;HTML 表单处理堪称基石般的存在&#xff0c;是构建交互性 Web 应用不可或缺的关键环节。从日常频繁使用的登录注册表单&#xff0c;到功能多样的搜索栏、反馈表单&#xff0c;HTML 表单如同桥梁&#xff0c;紧密连接着用户与 Web 应用…

C# CancellationTokenSource CancellationToken Task.Run传入token 取消令牌

基本使用方法创建 CancellationTokenSource获取 CancellationToken将 CancellationToken 传递给任务***注意*** 在任务中检查取消状态请求取消处理取消异常 高级用法设置超时自动取消或者使用 CancelAfter 方法关联多个取消令牌注册回调 注意事项 CancellationTokenSource 是 …

Git 之配置ssh

1、打开 Git Bash 终端 2、设置用户名 git config --global user.name tom3、生成公钥 ssh-keygen -t rsa4、查看公钥 cat ~/.ssh/id_rsa.pub5、将查看到的公钥添加到不同Git平台 6、验证ssh远程连接git仓库 ssh -T gitgitee.com ssh -T gitcodeup.aliyun.com

cli命令编写

新建文件夹 template-cli template-cli下运行 npm init生成package.json 新建bin文件夹和index.js文件 编写index.js #! /usr/bin/env node console.log(hello cli)package.json增加 bin 字段注册命令template-cli template-cli命令对应执行的内容文件 bin/index.js 运行 n…

vue3自定义动态锚点列表,实现本页面锚点跳转效果

需求&#xff1a;当前页面存在多个模块且内容很长时&#xff0c;需要提供一个锚点列表&#xff0c;可以快速查看对应模块内容 实现步骤&#xff1a; 1.每个模块添加唯一id&#xff0c;添加锚点列表div <template><!-- 模块A --><div id"modalA">…

L2TP实验

一、实验拓扑 二、实验内容 手工部署IPec VPN 三、实验步骤 1、配置接口IP和安全区域 [PPPoE Client]firewall zone trust [PPPoE Client-zone-trust]add int g 1/0/0[NAS]firewall zone untrust [NAS-zone-untrust]add int g 1/0/1 [NAS]firewall zone trust [NAS-zon…

青少年编程与数学 02-012 SQLite 数据库简介 01课题、数据库概要

青少年编程与数学 02-012 SQLite 数据库简介 01课题、数据库概要&#xff09; 一、特点二、功能 课题摘要:SQLite 是一种轻量级的嵌入式关系型数据库管理系统。 一、特点 轻量级 它不需要单独的服务器进程来运行。不像 MySQL 或 PostgreSQL 这样的数据库系统需要一个专门的服务…

分布式系统面试总结:3、分布式锁(和本地锁的区别、特点、常见实现方案)

仅供自学回顾使用&#xff0c;请支持javaGuide原版书籍。 本篇文章涉及到的分布式锁&#xff0c;在本人其他文章中也有涉及。 《JUC&#xff1a;三、两阶段终止模式、死锁的jconsole检测、乐观锁&#xff08;版本号机制CAS实现&#xff09;悲观锁》&#xff1a;https://blog.…

Ubuntu 系统上完全卸载 Docker

以下是在 Ubuntu 系统上完全卸载 Docker 的分步指南 一.卸载验证 二.卸载步骤 1.停止 Docker 服务 sudo systemctl stop docker.socket sudo systemctl stop docker.service2.卸载 Docker 软件包 # 移除 Docker 核心组件 sudo apt-get purge -y \docker-ce \docker-ce-cli …

Postman 版本信息速查:快速定位版本号

保持 Postman 更新至最新版本是非常重要的&#xff0c;因为这能让我们享受到最新的功能&#xff0c;同时也保证了软件的安全性。所以&#xff0c;如何快速查看你的 Postman 版本信息呢&#xff1f; 如何查看 Postman 的版本信息教程

EF Core 异步方法

文章目录 前言一、为什么使用异步方法二、核心异步方法1&#xff09;查询数据2&#xff09;保存数据3&#xff09;事务处理 三、异步查询最佳实践1&#xff09;始终使用 await2&#xff09;组合异步操作3&#xff09;并行查询&#xff08;谨慎使用&#xff09; 四、异常处理五、…

装饰器模式介绍和典型实现

装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许你通过将对象放入包含行为的特殊封装对象中来为原对象添加新的功能。装饰器模式的主要优点是可以在运行时动态地添加功能&#xff0c;而不需要修改原对象的代码。这使得代码更加灵活…

【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 中的日志管理:Logback 的集成

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、开篇整…

神经网络知识点整理

目录 ​一、深度学习基础与流程 二、神经网络基础组件 三、卷积神经网络&#xff08;CNN&#xff09;​编辑 四、循环神经网络&#xff08;RNN&#xff09;与LSTM 五、优化技巧与调参 六、应用场景与前沿​编辑 七、总结与展望​编辑 一、深度学习基础与流程 机器学习流…

【sql优化】where 1=1

文章目录 where 11问题描述错误实现正确实现性能对比测试 where 11 问题描述 在动态 SQL 拼接场景中&#xff0c;开发者常使用 WHERE 11 简化条件拼接逻辑&#xff08;避免处理首个条件的 AND&#xff09;。理论上&#xff0c;数据库优化器会忽略 11&#xff0c;但字符串拼接…

车载以太网网络测试 -24【SOME/IP概述】

目录 1 摘要2 车载SOME/IP 概述2.1发展背景以及应用2.1.1车载 SOME/IP 背景2.1.2 车载 SOME/IP 应用场景 2.3 什么是SOME/IP2.3.1 SOME/IP定义2.3.2 SOME/IP在协议栈中的位置 3 SOA是什么4 SOME/IP主要功能5 SOME/IP标准 1 摘要 本文主要介绍SOME/IP的背景以及在车载行业的发展…

vue3中,route4,获取当前页面路由的问题

首先应用场景如下&#xff1a; 在main.js里面&#xff0c;引入的是路由的配置文件&#xff0c;如下&#xff1a; import {router} from /router; app.use(router); 路由配置文件router.js如下&#xff1a; import { createRouter, createWebHistory } from vue-router; imp…

ip改变导致的数据库连接不上

前言 需要用到路由器&#xff0c;所以先把家里的路由器给拆了先用着。新的路由器到了之后&#xff0c;更换上新的路由器之后&#xff0c;调用到服务会有报错&#xff0c;记录一下更换路由器之后ip重新分配服务可能会报的错. 进一步可以看到有关网路在服务当中的影响。 正文 …

Chrome 开发环境快速屏蔽 CORS 跨域限制!

Chrome 开发环境快速屏蔽 CORS 跨域限制【详细教程】 ❓ 为什么需要临时屏蔽 CORS&#xff1f; 在前后端开发过程中&#xff0c;我们经常会遇到 跨域请求被浏览器拦截 的问题。例如&#xff0c;你在 http://localhost:3000 调用 https://api.example.com 时&#xff0c;可能会…

【力扣hot100题】(009)和为K的子数组

还是太菜了&#xff08;我&#xff09;&#xff0c;写了半天滑动窗口&#xff0c;然后看了答案又写了半天时间超限…… 总之就是记录每前n个子串的和&#xff0c;然后使用hash存储和为某个值出现的次数&#xff0c;每次求得新和就看看是否存在前面新和-k的字符&#xff0c;有的…