分布式ID生成系统之雪花算法详解

在当今的云计算和微服务架构盛行的时代,分布式系统已成为软件开发的重要组成部分。随着系统规模的扩大和业务的复杂化,对数据一致性和唯一性的要求也越来越高,尤其是在全局唯一标识符(ID)的生成上。因此,分布式ID生成系统应运而生,成为保证数据唯一性和提高系统可扩展性的关键技术之一。雪花算法(Snowflake)是Twitter开源的一种算法,用于生成64位的全局唯一ID,非常适用于分布式系统中生成唯一标识符。下面我们将深入探讨雪花算法的原理、结构和实现方式。

snowflake.jpg

分布式ID的需求背景

在单体应用中,生成唯一ID相对简单,如数据库的自增ID、UUID等方法可以轻易满足需求。然而,在分布式系统中,由于数据可能分布在不同的节点上,传统的ID生成方法面临着多方面的挑战:

  • 高可用性:系统任何一个部分的故障都不能影响ID的生成和使用。
  • 高并发:在大规模并发的场景下,ID生成系统必须能够快速响应。
  • 有序性:在某些业务场景中,需要ID具有一定的可排序性,以便于后续处理。
  • 低延迟:ID生成的延迟必须足够低,以满足实时性要求。

推特的雪花系统

Twitter开源的Snowflake算法是目前最流行的分布式ID生成方案之一。它通过结合时间戳、机器标识和序列号来生成64位的长整型ID,既保证了全局唯一性,又具有良好的有序性,非常适合高并发的场景。

如下展示了一个64为ID的构成:

snowflake.png

每个部分的含义如下:

  • 符号位(1位)

    始终为0,预留位,将来有可能用来区分有符号数和无符号数。

  • 时间戳(41位)

    它是纪元或者自定义纪元开始以来的毫秒数,Snowflake使用的是2010-11-04 01:42:54 的时间戳作为默认纪元的,我们也可以自定义。即时间戳为当前时间的毫秒数 - 自定义的纪元的毫秒数。

  • 数据中心id(5位)

    最多可以有 2 5 2^5 25个数据中心,取值范围为 0~31。

  • 机器id(5位)

    最多可以有 2 5 2^5 25台机器,取值范围为 0~31。

  • 序列号(12位)

    对于某个服务,每一个毫秒内生成一个id,序列号就加1,这个数字在每毫秒开始时都会被重置为0。即一个毫秒内单个服务可以生成 2 12 2^{12} 212即4096个id。

    我们可以根据服务的具体情况调整下id各部分的长度,比如,对于并发量低,单次生成id数量大的应用,我们可以适当减少数据中心id和机器id的位数,增加序列号位数来提高每个毫秒内id的生成数量。

Java实现雪花雪花系统

使用Java语言实现雪花算法的ID生成器,可以参考以下代码。这个实现同样遵循了雪花算法的基本结构,包括1位符号位、41位时间戳、10位机器标识(5位数据中心ID和5位工作机器ID)以及12位序列号。我们将这些位数放在了配置文件中,家人们可以根据实际情况进行调整。在这个代码中,我们提供了单id生成接口和批量id生成接口。代码如下:

  • 配置信息 application.yml
server:port: 8000snowflake:#数据中心id位数datacenterBits: 5# 机器id位数workerBits: 5# 序列id所占位数sequenceBits: 12# 数据中心id,范围0-2^5-1datacenterId: 1# 机器id,范围0-2^5-1workerId: 1# 时间戳起始点(2024-01-01 00::00:00 的毫秒数)twepoch: 1704038400000#单次批量生成id的最大数量  默认10万maxBatchCount: 100000
  • SnowflakeProperties
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Component
@ConfigurationProperties(prefix ="snowflake")
@Data
public class SnowflakeProperties {//数据中心idprivate Long datacenterId;//数据中心id位数private Long datacenterBits;//机器idprivate Long workerId;//机器id位数private Long workerBits;//序列id所占位数private Long sequenceBits;// 时间戳起始点(毫秒)private  Long twepoch;//单次批量生成id的最大数量private Integer maxBatchCount;
}
  • SnowflakeIdGenerator
package cn.xj.snowflake.generator;import cn.xj.snowflake.config.SnowflakeProperties;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;@Component
public class SnowflakeIdGenerator {//数据中心idprivate final long datacenterId;//数据中心id位数private final long datacenterBits;//机器idprivate final long workerId;//机器id位数private final long workerBits;//序列id所占位数private final  long sequenceBits;// 时间戳起始点(毫秒)private final long twepoch;//数据中心最大idprivate final long maxDatacenterId;//机器最大idprivate final long maxWorkerId;//最大序列号private final long maxSequence;//机器id左移位数private final long workerIdShift;//数据中心id左移位数private final long datacenterIdShift;//毫秒数左移位数private final long timestampLeftShift;//单次批量生成id的最大数量private final int maxBatchCount;// 序列号private long sequence = 0L;// 上一次时间戳private long lastTimestamp = -1L;public SnowflakeIdGenerator(SnowflakeProperties properties) {//数据中心idthis.datacenterId = properties.getDatacenterId();//数据中心id位数this.datacenterBits = properties.getDatacenterBits();//机器idthis.workerId = properties.getWorkerId();//机器id位数this.workerBits = properties.getWorkerBits();//序列id所占位数this.sequenceBits = properties.getSequenceBits();// 时间戳起始点(毫秒)this.twepoch = properties.getTwepoch();//数据中心最大idthis.maxDatacenterId = -1L ^ (-1L << properties.getDatacenterBits());//机器最大idthis.maxWorkerId = -1L ^ (-1L << properties.getWorkerBits());//最大序列号this.maxSequence = -1L ^ (-1L << properties.getSequenceBits());this.workerIdShift = properties.getSequenceBits();//数据中心id左移位数this.datacenterIdShift = properties.getSequenceBits() + properties.getWorkerBits();//毫秒数左移位数this.timestampLeftShift = properties.getSequenceBits() + properties.getWorkerBits() + properties.getSequenceBits();//单次批量生成id的最大数量this.maxBatchCount = properties.getMaxBatchCount();// 校验datacenterId和workerId是否超出最大值if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.format("数据中心Id不能大于%d或小于0", maxDatacenterId));}if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("机器Id不能大于%d或小于0", maxWorkerId));}}/*** id生成方法(单个)* @return*/public synchronized long nextId() {//获取当前时间的毫秒数long timestamp = currentTime();//判断时钟是否回拨if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("时钟回拨,回拨毫秒数:%d", lastTimestamp - timestamp));}//设置序列号if (lastTimestamp == timestamp) {//设置序列号递增,如果当前毫秒内序列号已经达到最大值,则直到下一毫秒在重新从0开始计算序列号sequence = (sequence + 1) & maxSequence;if (sequence == 0) {timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;//计算idreturn ((timestamp - twepoch) << timestampLeftShift) |(datacenterId << datacenterIdShift) |(workerId << workerIdShift) |sequence;}/*** id生成方法(批量)* @return*/public synchronized List<Long> nextIds(int count) {if (count > maxBatchCount || count < 0) {throw new IllegalArgumentException(String.format("批量生成id的数量不能大于%d或小于0", maxBatchCount));}List<Long> ids = new ArrayList<>(count);for (int i = 0; i < count; i++) {ids.add(nextId());}return ids;}/*** 循环等待直至获取到新的毫秒时间戳* 确保生成的时间戳总是向前移动的,即使在相同的毫秒内请求多个ID时也能保持唯一性。*/private long tilNextMillis(long lastTimestamp) {long timestamp = currentTime();// 循环等待直至获取到新的毫秒时间戳while (timestamp <= lastTimestamp) {timestamp = currentTime();}return timestamp;}/*** 获取当前时间的毫秒数*/private long currentTime() {return System.currentTimeMillis();}}

这个Java类SnowflakeIdWorker封装了雪花算法的核心逻辑。它允许通过构造函数指定数据中心ID和机器ID,并提供了nextId()和nextIds()方法用于生成唯一的ID。该方法通过同步关键字synchronized保证了线程安全。

  • SnowflakeApi

import cn.xj.snowflake.generator.SnowflakeIdGenerator;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
public class SnowflakeApi {@Resourceprivate SnowflakeIdGenerator snowflakeIdGenerator;@PostMapping("/snowflake/api/nextId")public Long nextId(){return snowflakeIdGenerator.nextId();}@PostMapping("/snowflake/api/nextIds")public List<Long> nextIds(@RequestBody int count){return snowflakeIdGenerator.nextIds(count);}
}

接口调用详情

单个id生成接口nextId:

_20240303211115.jpg

批量id生成接口nextIds:我们此处生成了10万条id,响应时长不到1s
_20240303211339.jpg

雪花算法的开源代码或者优秀代码示例有很多,但思想基本是一样的。这有篇美团的文章,大家也可以参考下美团的leaf [Leaf——美团点评分布式ID生成系统: https://tech.meituan.com/2017/04/21/mt-leaf.html](https://tech.meituan.com/2017/04/21/mt-leaf.html)

总结

雪花算法作为一种高效、简单的分布式系统ID生成方案,已经被广泛应用于各种互联网产品和系统中。它解决了分布式环境下ID生成的唯一性、时序性和高性能的问题。随着互联网技术的不断进步和发展,分布式ID生成系统将继续演化,但雪花算法作为其中的经典之作,其核心思想和设计理念将长久影响这一领域。

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

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

相关文章

yolov5训练太慢的解决方案

问题原因 训练太慢大多是因为没有安装CUDA和pytorch&#xff0c;导致的只有cpu在跑&#xff0c;显卡没跑 这就是很典型的。 解决方案 第一步&#xff1a;安装CUDA 在本机上面安装CUDA,记住只有N卡可以安装&#xff0c;一开始的电脑是自带CUDA的。 如果不是自带的CUDA&…

求数字的每一位之和

求数字的每一位之和 题目描述&#xff1a;解法思路&#xff1a;解法代码&#xff1a;运行结果&#xff1a; 题目描述&#xff1a; 输入一个整数m&#xff0c;求这个整数m的每⼀位之和&#xff0c;并打印。 测试1&#xff1a; 输⼊&#xff1a;1234 输出&#xff1a;10 测试2&…

土壤侵蚀量化评估

根据之前的文章,已经算出了R、K、LS、C、P 现在计算土壤侵蚀,将几个前期制作好的因子的TIFF文件,用栅格计算器相乘 发现局部地区存在轻度侵蚀,大部分区域是微度侵蚀 然后对比了一下范围 其中的几个因子都在文献范围内,说明计算结果并未出错,可能就是研究区正常范围和结…

6020一拖二快充线:手机充电的革命性创新

在快节奏的现代生活中&#xff0c;手机已不仅仅是一个通讯工具&#xff0c;更是我们工作、学习和娱乐的得力助手。然而&#xff0c;手机的电量问题一直是困扰着我们的难题。为了解决这个问题&#xff0c;市场上出现了一种名为“一拖二快充线”的充电设备&#xff0c;它不仅具备…

八. 实战:CUDA-BEVFusion部署分析-分析BEVFusion中各个ONNX

目录 前言0. 简述1. camera.backbone.onnx(fp16)2. camera.backbone.onnx(int8)3. camera.vtransform.onnx(fp16)4. fuser.onnx(fp16)5. fuser.onnx(int8)6. lidar.backbone.xyz.onnx7. head.bbox.onnx(fp16)总结下载链接参考 前言 自动驾驶之心推出的《CUDA与TensorRT部署实战…

Unity UGUI之Scrollbar基本了解

Unity的Scrollbar组件是用于在UI中创建滚动条的组件之一。滚动条通常与其他可滚动的UI元素&#xff08;如滚动视图或列表&#xff09;一起使用&#xff0c;以便用户可以在内容超出可见区域时滚动内容。 以下是Scrollbar的基本信息和用法: 1、创建 在Unity的Hierarchy视图中右…

jetson nano——编译安装opencv==4.4

目录 1.下载源码&#xff0c;我提供的链接如下&#xff1a;1.1文件上传的路径位置&#xff0c;注意ymck是我自己的用户名&#xff08;你们自己换成你们自己相对应的就行&#xff09; 2.解压文件3.安装依赖4.增加swap交换内存4.1临时增加交换内存swap4.2永久增加swap 5.安装open…

2024-03-03 作业

作业要求&#xff1a; 1.使用fwrite、fread将一张随意的bmp图片&#xff0c;修改成德国的国旗 2.使用提供的getch函数&#xff0c;编写一个专门用来输入密码的函数&#xff0c;要求输入密码的时候&#xff0c;显示 * 号&#xff0c;输入回车的时候&#xff0c;密码输入结束 作业…

【MySQL】索引(重点)-- 详解

一、索引 没有索引&#xff0c;可能会有什么问题&#xff1f; 索引 &#xff1a;提高数据库的性能&#xff0c;索引是物美价廉的东西了。不用加内存&#xff0c;不用改程序&#xff0c;不用调 sql &#xff0c;只要执行正确的 create index &#xff0c;查询速度就可能提高成…

加密与安全_探索数字证书

文章目录 Pre概述使用keytool生成证书使用Openssl生成证书 &#xff08;推荐&#xff09;证书的吊销小结 Pre PKI - 借助Nginx 实现Https 服务端单向认证、服务端客户端双向认证 PKI - 04 证书授权颁发机构&#xff08;CA&#xff09; & 数字证书 PKI - 数字签名与数字证…

java面试题(spring框架篇)(黑马 )

树形图&#xff1a; 一、Spring框架种的单例bean是线程安全吗&#xff1f; Service Scope("singleton") public class UserServiceImpl implements UserService{ } singleton:bean在每个Spring IOC容器中只有一个实例 protype&#xff1a;一个bean的定义可以有多个…

【Web安全靶场】xss-labs-master 1-20

xss-labs-master 其他靶场见专栏 文章目录 xss-labs-masterlevel-1level-2level-3level-4level-5level-6level-7level-8level-9level-10level-11level-12level-13level-14level-15level-16level-17level-18level-19level-20 level-1 第一关没有进行任何限制&#xff0c;get请求…

pytorch_神经网络构建6

文章目录 强化学习概念实现qLearning基于这个思路,那么解决这个问题的代码如下 强化学习概念 强化学习有一个非常直观的表现&#xff0c;就是从出发点到目标之间存在着一个连续的状态转换&#xff0c;比如说从状态一到状态456&#xff0c;而每一个状态都有多种的行为&#xff…

全国青少年软件编程(Python)等级考试试卷(一级) 测试卷2021年12月

第 1 题 【 单选题 】 下面程序的运行结果是什么&#xff1f;&#xff08; &#xff09; a10 b5 ca*b print(c) A :10 B :15 C :50 D :5 正确答案:C 试题解析: 第 2 题 【 单选题 】 与a>b and b>c等价的是&#xff1f;&#xff08; &#xff09; A…

Libevent的使用及reactor模型

Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库&#xff0c;主要有以下几个亮点&#xff1a;事件驱动&#xff08; event-driven&#xff09;&#xff0c;高性能;轻量级&#xff0c;专注于网络&#xff0c;不如 ACE 那么臃肿庞大&#xff1b;源代码相当精炼、易读…

aop监控spring cloud接口超时,并记录到数据库

引入pom <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0…

宝塔面板安装各种组件以及部署应用服务

在linux服务器安装宝塔面板 一、从宝塔官网下载exe安装包&#xff0c;安装命令从宝塔官网&#xff08;https://www.bt.cn/&#xff09;获取 yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh二、安…

自动驾驶加速落地,激光雷达放量可期(上)

1 激光雷达应用广泛&#xff0c;汽车有望成最大催化 激光雷达&#xff08;LiDAR&#xff09;是一种主动遥感技术&#xff0c;通过测定传感器发出的激光在传感器与目标物体之间的传播距离&#xff0c;来分析目标地物表面的反射能量大小、反射波谱的幅度、频率和相位等信息&#…

一文扫盲:订单管理系统,订单是公司生命线。

hello&#xff0c;我是贝格前端工场&#xff0c;本期给大家分享订单管理系统的知识点&#xff0c;欢迎老铁们点赞、关注&#xff0c;如有需求可以私信我们。 一、什么是订单管理系统 单管理系统是一种用于管理和处理订单的软件系统。它通常用于企业、电子商务平台、零售店等需…

技术小知识:面向对象和过程的区别 ⑤

一、思想区别 面相对象&#xff1a;始终把所有事情思考归类、抽离封装成对象来调用完成。 面向过程&#xff1a;直接平铺展开按顺序执行完成任务。 面向对象多了很多对象的创建、使用&#xff0c;销毁的过程资源消耗。是一种模块化编程思想。 https://www.cnblogs.com/kuangmen…