雪花算法:分布式ID生成的优雅解决方案(建议收藏)
在分布式系统中,唯一ID的生成和管理是一项重要而棘手的任务。雪花算法,由Twitter开源的一种分布式ID生成算法,为这个问题提供了一种优雅的解决方案。本文将详细介绍雪花算法的原理、设计和实现,并通过示例代码和图片帮助读者更好地理解。
一、雪花算法的基本概念
雪花算法是一种全局ID生成算法,其核心思想是将64位的long型ID分为四个部分,分别为:时间戳、工作机器ID、数据中心ID和序列号。通过将数据映射到具有特定结构的分布式系统中,实现数据的存储和查询。该算法由一系列节点组成,每个节点负责存储数据的一部分。这些节点通过哈希函数将数据映射到特定的位置,形成类似于雪花结构的分布式系统。通过这种方式,雪花算法能够在分布式系统中保证ID的唯一性和有序性。
二、雪花算法具有以下优点:
- 易于扩展:可以方便地添加或删除节点,适应数据量的变化。
- 容错性高:即使部分节点发生故障,整个系统仍可正常运行。
- 负载均衡:数据在节点间分布均匀,有效利用系统资源。
- 适用于各种数据访问模式:支持随机访问和顺序访问等访问模式。
三、雪花算法的设计与分析
- 时间戳
时间戳是ID中的最高位,占据了整个ID的41位。这使得雪花算法能够支持未来数十年的唯一性。时间戳部分还提供了排序的功能,可以根据时间戳来对数据进行排序。
- 工作机器ID
工作机器ID占据了ID的10位,可以支持最多1024个工作节点。这使得在同一台机器上运行的不同应用程序实例可以使用不同的工作机器ID来生成唯一的ID。
- 数据中心ID
数据中心ID占据了ID的5位,可以支持最多32个数据中心。这使得在不同数据中心运行的应用程序可以使用不同的数据中心ID来生成唯一的ID。
- 序列号
序列号占据了ID的12位,可以支持每个节点每毫秒产生4096个唯一的ID。这使得在同一台机器上运行的不同应用程序实例可以生成唯一的ID,即使在毫秒级别内也能保证唯一性。
四、雪花算法的代码实现
以下是雪花算法的Java代码实现示例:
public class SnowflakeIdWorker{ /** 开始时间截 (2015-01-01) */private final long twepoch = 1288834974657L;/** 机器id所占的位数 */private final long workerIdBits = 5L;/** 数据标识id所占的位数 */private final long datacenterIdBits = 5L;/** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */private final long maxWorkerId = -1L ^ (-1L << workerIdBits);/** 支持的最大数据标识id,结果是31 */private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);/** 序列在id中占的位数 */private final long sequenceBits = 12L;/** 机器ID向左移12位 */private final long workerIdShift = sequenceBits;/** 数据标识id向左移17位(12+5) */private final long datacenterIdShift = sequenceBits + workerIdBits;/** 时间截向左移22位(5+5+12) */private final long timestampLeftShift = sequenceBits + workerIdBits+ datacenterIdBits;/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */private final long sequenceMask = -1L ^ (-1L << sequenceBits);/** 工作机器ID(0~31) */private long workerId;/** 数据中心ID(0~31) */private long datacenterId;/** 毫秒内序列(0~4095) */private long sequence = 0L;/** 上次生成ID的时间截 */private long lastTimestamp = -1L;/*** 构造函数* * @param workerId* 工作ID (0~31)* @param datacenterId* 数据中心ID (0~31)*/public SnowflakeId(long workerId, long datacenterId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0",maxWorkerId));}if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0",maxDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}/*** 获得下一个ID (该方法是线程安全的)* * @return SnowflakeId*/public synchronized long nextId() {long timestamp = timeGen();// 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",(lastTimestamp - timestamp)));}// 如果是同一时间生成的,则进行毫秒内序列if (lastTimestamp == timestamp) {sequence = (sequence + 1) & sequenceMask;// 毫秒内序列溢出if (sequence == 0) {// 阻塞到下一个毫秒,获得新的时间戳timestamp = tilNextMillis(lastTimestamp);}}// 时间戳改变,毫秒内序列重置else {sequence = 0L;}// 上次生成ID的时间截lastTimestamp = timestamp;// 移位并通过或运算拼到一起组成64位的IDreturn ((timestamp - twepoch) << timestampLeftShift) //| (datacenterId << datacenterIdShift) //| (workerId << workerIdShift) //| sequence;}/*** 阻塞到下一个毫秒,直到获得新的时间戳* * @param lastTimestamp* 上次生成ID的时间截* @return 当前时间戳*/protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}/*** 返回以毫秒为单位的当前时间* * @return 当前时间(毫秒)*/protected long timeGen() {return System.currentTimeMillis();}//测试方法自己写}
在这个代码中,首先定义了雪花算法的各个组成部分,包括时间戳、工作机器id、数据中心id和序列号。然后,根据这些组成部分计算出一个唯一的ID。在生成ID的过程中,需要考虑时间戳的回拨问题,如果当前时间小于上一次生成ID的时间戳,那么就抛出一个异常。同时,在同一毫秒内,如果生成的ID数量达到上限(2^12个),那么就等待下一毫秒再生成。
五、注意事项
雪花算法(Snowflake Algorithm)在系统运行时只需要调用一次,然后通过SnowflakeIdInit.snowflakeId.nextId()
自增来生成唯一的ID。
雪花算法是一种分布式唯一ID生成器,它基于Twitter的雪花算法(Snowflake Algorithm)实现。该算法通过生成一个64位的ID来确保在分布式系统中生成唯一的ID。
在雪花算法中,ID被划分为多个部分,包括时间戳、机器标识和序列号等。首次调用时,会根据当前时间戳、机器标识和序列号生成一个唯一的ID。之后,每次调用nextId()
方法时,会根据上次生成的ID计算出下一个ID。
具体来说,SnowflakeIdInit.snowflakeId.nextId()
方法会根据上次生成的ID,增加一定的值(通常是1),然后生成一个新的ID。这个新的ID会比上次生成的ID更大,因为时间戳部分会随着时间的推移而增加。
需要注意的是,雪花算法生成的ID是单调递增的,并且具有较好的分布性和扩展性。但是,由于机器标识和序列号的长度有限,所以在某些情况下可能会出现ID冲突的情况。为了解决这个问题,可以引入冲突检测机制或者使用其他更高级的分布式唯一ID生成器。
六、雪花算法在单机和集群模式下的区别
雪花算法(Snowflake)是Twitter开发的一种生成全局唯一ID的算法。它通过一个64位的long型数字作为全局唯一ID,由时间戳、机器标识和序列号三部分组成。
在单机模式下,雪花算法不需要考虑分布式环境的因素,因此不会引入额外的网络开销。此外,由于单机环境中的机器数量有限,可以预先定义机器标识,因此不需要在运行时动态生成机器标识。
然而,在集群模式下,雪花算法需要考虑分布式环境的因素。首先,由于集群中的机器数量可能很大,无法预先定义所有机器的标识,因此需要在运行时动态生成机器标识。其次,由于集群中的机器可能分布在不同的数据中心,因此需要在机器标识中包含数据中心标识,以便区分不同的数据中心。此外,由于网络开销的存在,可能需要引入额外的机制来保证全局唯一ID的生成。
总的来说,雪花算法在单机和集群模式下的主要区别在于是否需要考虑到分布式环境的因素,以及是否需要动态生成机器标识。此外,在集群模式下,还需要考虑如何保证全局唯一ID的生成。
七、在分布式环境下,如果仍然使用单机版的雪花算法,可能会导致以下问题:
- 唯一性问题:单机版的雪花算法生成的ID只在一个机器上是唯一的,而在分布式环境下,不同的机器使用相同的雪花算法可能会生成重复的ID。这会导致无法保证全局唯一性。
- 性能问题:在分布式环境下,如果每台机器都生成全局唯一的ID,会产生大量的网络开销。因为每台机器都需要将自己的ID发送给其他机器以避免冲突,这会增加网络拥堵和延迟。
- 可扩展性问题:随着机器数量的增加,单机版的雪花算法生成的ID可能很快耗尽。因为64位的ID只有2^64个可能的值,如果多台机器同时生成ID,可能会很快达到上限。
因此,在分布式环境下,需要使用更适用于多机器的雪花算法版本,以保证全局唯一性、减少网络开销和提高可扩展性。例如,可以引入数据中心标识和机器标识来区分不同的机器和数据中心,并在生成ID时考虑时间戳、机器标识和序列号等因素。此外,还可以使用一致性哈希等算法来分配ID生成任务,以避免单台机器负担过重的情况。
八、在集群模式下如何使用雪花算法
在使用Snowflake算法时,每个机器都需要有一个唯一的机器标识。可以将机器标识作为参数传递给SnowflakeId类的构造函数,以初始化一个SnowflakeId实例。然后,通过调用实例的nextId()方法来生成下一个唯一的ID。
在集群环境下,可以按照以下步骤使用SnowflakeId类:
- 确定每个机器的唯一标识:可以使用机器的IP地址或主机名作为机器标识。确保每个机器的标识都是唯一的,以避免ID冲突。
- 创建SnowflakeId实例:在每个机器上创建一个SnowflakeId实例,将机器标识作为参数传递给构造函数。
long machineId = getMachineId(); // 获取机器标识,可以是IP地址或主机名等唯一标识
long dataCenterId = getDataCenterId(); // 获取数据中心标识,可以是其他唯一标识
SnowflakeId snowflakeId = new SnowflakeId(dataCenterId, machineId);
- 生成唯一ID:通过调用SnowflakeId实例的nextId()方法来生成下一个唯一的ID。例如:
long uniqueId = snowflakeId.nextId();
生成的唯一ID由时间戳、数据中心标识、机器标识和序列号组成,保证了在多机器环境下的唯一性。
需要注意的是,在多机器环境下,需要确保每个机器的时钟同步,以避免时间戳导致的ID冲突。此外,还需要确保每个机器的机器标识都是唯一的,以避免机器之间的ID冲突。
代码示例如下:
import java.util.StringTokenizer; public class MachineIdentifier {private static Logger log = Logger.getLogger(MachineIdentifier.class);public static SnowflakeIdWorker snowflakeId;private static String ipString = "192.168.1.1,192.168.1.2,192.168.1.3";static {try {if (snowflakeId == null) {//获取本机的IP地址,并将其以字符串形式存储InetAddress address = InetAddress.getLocalHost();String host = address.getHostAddress();//创建一个新的对象sc,并用逗号(,)作为分隔符,将ipString这个字符串分解为多个子字符串,这些子字符串保存在sc中。StringTokenizer sc = new StringTokenizer(ipString, ",");int num = 1;//遍历比较IP地址while (sc.hasMoreTokens()) {String s = sc.nextToken();if (s.equals(host)) {snowflakeId = new SnowflakeIdWorker(num, num);}num++;}//无论是否找到匹配的IP地址,都会创建一个新的SnowflakeId对象,使用当前的目标编号(num)作为参数。snowflakeId = new SnowflakeIdWorker(num, num);}} catch (UnknownHostException e) {throw new RuntimeException(e);}}public static SnowflakeIdWorker getSnowflakeId(){return snowflakeId;}}
八、雪花算法的未来发展
随着云计算、大数据等技术的不断发展,雪花算法在未来将面临更多的挑战和机遇。未来的研究将集中在以下几个方面:
- 可扩展性:随着数据量的不断增加,如何提高雪花算法的可扩展性成为了一个亟待解决的问题。未来的研究将致力于优化哈希函数和处理大规模数据的性能。
- 容错性:在分布式系统中,节点故障是不可避免的。未来的研究将探索如何提高雪花算法的容错性,以便在节点故障时仍能保持系统的可用性和可靠性。
- 数据访问模式:不同的数据访问模式会对雪花算法的性能产生影响。未来的研究将关注如何优化雪花算法以适应各种数据访问模式,从而提高系统的整体性能。
- 安全性:在分布式系统中,数据的安全性和隐私保护是一个重要的关注点。未来的研究将致力于研究如何在保证数据安全性的同时,实现高效的数据查询和处理。
- 能耗问题:在分布式系统中,节点的能耗是一个值得关注的问题。未来的研究将探索如何优化雪花算法以降低节点的能耗,从而提高系统的可持续性。
九、总结
本文详细探讨了雪花算法在单机和集群模式下的应用。通过深入剖析雪花算法的基本概念、理论知识及其在不同场景下的应用特点,我们充分理解了该算法的原理、优势以及适用场景。同时,本文还分析了雪花算法在实际应用中的重要性和必要性,并探讨了未来的研究方向和趋势。希望本文能为读者提供有关雪花算法的全面认识,并为相关领域的研究提供有益的参考。
相关文章:

雪花算法:分布式ID生成的优雅解决方案(建议收藏)
在分布式系统中,唯一ID的生成和管理是一项重要而棘手的任务。雪花算法,由Twitter开源的一种分布式ID生成算法,为这个问题提供了一种优雅的解决方案。本文将详细介绍雪花算法的原理、设计和实现,并通过示例代码和图片帮助读者更好地…...

全国产EtherCAT运动控制边缘控制器(六):RtBasic文件下载与连续轨迹加工的Python+Qt开发
今天,正运动小助手给大家分享一下全国产EtherCAT运动控制边缘控制器ZMC432H如何使用PythonQT实现连续轨迹加工。 01 功能简介 全国产EtherCAT运动控制边缘控制器ZMC432H是正运动的一款软硬件全国产自主可控,运动控制接口兼容EtherCAT总线和脉冲型的独立…...
git代码行统计
本文介绍统计项目代码行的方式,包括使用git log统计、git ls-files统计和使用linux命令行方式统计。 一、使用git log统计 1.统计所有代码行数 当前代码都存放在git仓库下,当需进行代码行数统计时,让开发人员在代码路径下运行如下指令&…...

LEEDCODE 2235两整数相加
class Solution { public:int sum(int num1, int num2) {return (num1 num2);} };...

魔术般的速度,焕然一新的磁盘空间 - Magic Disk Cleaner for Mac 2023
在当今这个信息时代,我们的磁盘空间无时无刻不在被各种文件和数据所填满。无论是工作文件,还是日常生活的照片、视频,亦或是下载的各种应用程序,都在不断地蚕食着我们的磁盘空间。面对这种情况,一款高效、便捷的磁盘垃…...
项目切换多租户导致的数据库SQL执行异常
先贴异常日志 java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(D…...

安防视频监控平台EasyCVR服务器需要开启firewalld防火墙,该如何开放端口?
智能视频监控/视频云存储/集中存储/视频汇聚平台EasyCVR具备视频融合汇聚能力,作为安防视频监控综合管理平台,它支持多协议接入、多格式视频流分发,视频监控综合管理平台EasyCVR支持海量视频汇聚管理,可应用在多样化的场景上&…...

Ubuntu Desktop 20.04升级gcc-11
默认自带的gcc是9,需要升级到11 sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt install gcc-11 sudo apt install g11 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 50 sudo update-alternatives -…...

网站如何改成HTTPS访问
在今天的互联网环境中,将网站更改成HTTPS访问已经成为了一种标准做法。HTTPS不仅有助于提高网站的安全性,还可以提高搜索引擎排名,并增强用户信任。因此,转换为HTTPS是一个重要的举措,无论您拥有个人博客、电子商务网站…...

LeetCode 996.正方形数组的数目
和上一道状压的区别在于我们要去重一下~ 思路都是和上一篇博客是一样的,感兴趣的同学可以看一下 const int N 15; int dp[1<<N][N]; int n; vector<int>nums1;bool check(int x){int tem sqrt(x);if(tem*temx)return 1;return 0; }int dfs(int u,in…...

vue3写nav滚动事件中悬停在顶部
1. 防抖类Animate, 使用requestAnimationFrame代替setTimeout 也可以使用节流函数, lodash有现成的防抖和节流方法 _.debounce防抖 _.throttle节流 export default class Animate {constructor() {this.timer null;}start (fn) > {if (!fn) {throw new Error(需要执行…...
关于qiling->UC_ERR_FETCH_UNMAPPED等执行EXE时内存错误的问题
该文章遇到的问题简述: 使用wsl虚拟机,正常走了qiling配置流程后无法使用qiling对样例之外的exe进行模拟,会在执行到dll时,在dll的代码中报出内存未分配等读写错误。系统:kali、ubuntu20、ubuntu22。 解决࿱…...
语言模型和人类的推理都依赖内容
人类不太擅长逻辑,需要依赖内容直觉进行推理。许多认知研究表明,人类的推理并不是完全抽象的(不是完全的形式与内容分离)。 相反,我们的推理取决于问题的内容: 当内容支持逻辑推理时,我们回答…...

5.1 运输层协议概述
思维导图: 前言: 第5章 运输层笔记 1. 概览 主要内容:介绍运输层协议的特点、进程间通信、端口、UDP和TCP协议、可靠传输、TCP报文段的首部格式、TCP的关键概念(如滑动窗口、流量控制、拥塞控制和连接管理)。重要性…...

Jmeter保存csv数据文件出现乱码
在Jmeter的聚合报告中,点击“Save Table Data”,打开保存的CSV文件中文出现乱码。这是因为CSV文件的格式不是UTF-8导致。 解决办法如下: 方法:使用记事本打开csv文件(打开方式选择记事本),点击左上角菜单“文件-》另存…...
双闭环直流电机调速系统设计
要 在我们日常生活中,无刷直流电机随处可见,因为其相比其他电机而言结构相对简单,运行稳定且便于维修等优势,最重要的是直流电机在调速方面具有很好的优势。随着自动控制技术和微电子技术的不断革新,目前的技术水平为…...

[ poi-表格导出 ] java.lang.NoClassDefFoundError: org/apache/poi/POIXMLTypeLoader
解决报错: org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: org/apache/poi/POIXMLTypeLoader 报错描述: 表格导出本来使用正常,偶然就报了以上错误…...

基于FPGA的图像差分运算及目标提取实现,包含testbench和MATLAB辅助验证程序
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2022/07/28 01:51:…...
闭环思维笔记
这本书的情况我已经看完了,道理方面还是不错的,但案例方面跟我前几年在抖音看到的畅销书的案例一样,答辩,所以要看的人一定要根据不同的情况和场景去实施。 闭环思维的核心就是有始、有终、有反馈,在开始和结束过程中前…...

JMeter如何开展性能测试
文章目录 性能测试指标理解透彻以及测算微聊性能测试性能测试流程准备流程 👑作者主页:Java冰激凌 性能测试指标理解透彻以及测算 虚拟用户数: 线程 用户并发数:指在某一时间,一定数量的虚拟用户同时对系统的某个功…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...