雪花算法:分布式ID生成的优雅解决方案(建议收藏)

news2024/11/25 16:31:49

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

一、雪花算法的基本概念

雪花算法是一种全局ID生成算法,其核心思想是将64位的long型ID分为四个部分,分别为:时间戳、工作机器ID、数据中心ID和序列号。通过将数据映射到具有特定结构的分布式系统中,实现数据的存储和查询。该算法由一系列节点组成,每个节点负责存储数据的一部分。这些节点通过哈希函数将数据映射到特定的位置,形成类似于雪花结构的分布式系统。通过这种方式,雪花算法能够在分布式系统中保证ID的唯一性和有序性。

二、雪花算法具有以下优点:

  1. 易于扩展:可以方便地添加或删除节点,适应数据量的变化。
  2. 容错性高:即使部分节点发生故障,整个系统仍可正常运行。
  3. 负载均衡:数据在节点间分布均匀,有效利用系统资源。
  4. 适用于各种数据访问模式:支持随机访问和顺序访问等访问模式。

三、雪花算法的设计与分析

  1. 时间戳

时间戳是ID中的最高位,占据了整个ID的41位。这使得雪花算法能够支持未来数十年的唯一性。时间戳部分还提供了排序的功能,可以根据时间戳来对数据进行排序。

  1. 工作机器ID

工作机器ID占据了ID的10位,可以支持最多1024个工作节点。这使得在同一台机器上运行的不同应用程序实例可以使用不同的工作机器ID来生成唯一的ID。

  1. 数据中心ID

数据中心ID占据了ID的5位,可以支持最多32个数据中心。这使得在不同数据中心运行的应用程序可以使用不同的数据中心ID来生成唯一的ID。

  1. 序列号

序列号占据了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位的ID
        return ((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();
    }

    /** 测试 */
    public static void main(String[] args) {
        for (int i = 0; i < 100000; i++) {
            long id = SnowflakeIdWorker.getSnowflakeId().nextId();
            System.out.println(id);
        }
    }
   }

在这个代码中,首先定义了雪花算法的各个组成部分,包括时间戳、工作机器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的生成。

七、在分布式环境下,如果仍然使用单机版的雪花算法,可能会导致以下问题:

  1. 唯一性问题:单机版的雪花算法生成的ID只在一个机器上是唯一的,而在分布式环境下,不同的机器使用相同的雪花算法可能会生成重复的ID。这会导致无法保证全局唯一性。
  2. 性能问题:在分布式环境下,如果每台机器都生成全局唯一的ID,会产生大量的网络开销。因为每台机器都需要将自己的ID发送给其他机器以避免冲突,这会增加网络拥堵和延迟。
  3. 可扩展性问题:随着机器数量的增加,单机版的雪花算法生成的ID可能很快耗尽。因为64位的ID只有2^64个可能的值,如果多台机器同时生成ID,可能会很快达到上限。

因此,在分布式环境下,需要使用更适用于多机器的雪花算法版本,以保证全局唯一性、减少网络开销和提高可扩展性。例如,可以引入数据中心标识和机器标识来区分不同的机器和数据中心,并在生成ID时考虑时间戳、机器标识和序列号等因素。此外,还可以使用一致性哈希等算法来分配ID生成任务,以避免单台机器负担过重的情况。

八、在集群模式下如何使用雪花算法

在使用Snowflake算法时,每个机器都需要有一个唯一的机器标识。可以将机器标识作为参数传递给SnowflakeId类的构造函数,以初始化一个SnowflakeId实例。然后,通过调用实例的nextId()方法来生成下一个唯一的ID。

在集群环境下,可以按照以下步骤使用SnowflakeId类:

  1. 确定每个机器的唯一标识:可以使用机器的IP地址或主机名作为机器标识。确保每个机器的标识都是唯一的,以避免ID冲突。
  2. 创建SnowflakeId实例:在每个机器上创建一个SnowflakeId实例,将机器标识作为参数传递给构造函数。
long machineId = getMachineId(); // 获取机器标识,可以是IP地址或主机名等唯一标识  
long dataCenterId = getDataCenterId(); // 获取数据中心标识,可以是其他唯一标识  
SnowflakeId snowflakeId = new SnowflakeId(dataCenterId, machineId);
  1. 生成唯一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)) {
						log.error("雪花算法-本机ip地址:{" + host + "},存档地址:{" + s + "},目标编号{" + num + "}");
						snowflakeId = new SnowflakeIdWorker(num, num);
					}
					num++;
				}
				log.error("雪花算法初始化异常-本机ip地址:{"+ host + "}");
				//无论是否找到匹配的IP地址,都会创建一个新的SnowflakeId对象,使用当前的目标编号(num)作为参数。
				snowflakeId = new SnowflakeIdWorker(num, num);
			}
		} catch (UnknownHostException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static SnowflakeIdWorker getSnowflakeId(){
		return snowflakeId;
	}

	
}

八、雪花算法的未来发展

随着云计算、大数据等技术的不断发展,雪花算法在未来将面临更多的挑战和机遇。未来的研究将集中在以下几个方面:

  1. 可扩展性:随着数据量的不断增加,如何提高雪花算法的可扩展性成为了一个亟待解决的问题。未来的研究将致力于优化哈希函数和处理大规模数据的性能。
  2. 容错性:在分布式系统中,节点故障是不可避免的。未来的研究将探索如何提高雪花算法的容错性,以便在节点故障时仍能保持系统的可用性和可靠性。
  3. 数据访问模式:不同的数据访问模式会对雪花算法的性能产生影响。未来的研究将关注如何优化雪花算法以适应各种数据访问模式,从而提高系统的整体性能。
  4. 安全性:在分布式系统中,数据的安全性和隐私保护是一个重要的关注点。未来的研究将致力于研究如何在保证数据安全性的同时,实现高效的数据查询和处理。
  5. 能耗问题:在分布式系统中,节点的能耗是一个值得关注的问题。未来的研究将探索如何优化雪花算法以降低节点的能耗,从而提高系统的可持续性。

九、总结

本文详细探讨了雪花算法在单机和集群模式下的应用。通过深入剖析雪花算法的基本概念、理论知识及其在不同场景下的应用特点,我们充分理解了该算法的原理、优势以及适用场景。同时,本文还分析了雪花算法在实际应用中的重要性和必要性,并探讨了未来的研究方向和趋势。希望本文能为读者提供有关雪花算法的全面认识,并为相关领域的研究提供有益的参考。

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

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

相关文章

Asterisk Ubuntu 安装

更新环境 sudo apt update sudo apt install wget build-essential git autoconf subversion pkg-config libtool sudo contrib/scripts/get_mp3_source.sh A addons/mp3 A addons/mp3/common.c A addons/mp3/huffman.h A addons/mp3/tabinit.c A addons/mp3/Ma…

竞赛选题 深度学习大数据物流平台 python

文章目录 0 前言1 课题背景2 物流大数据平台的架构与设计3 智能车货匹配推荐算法的实现**1\. 问题陈述****2\. 算法模型**3\. 模型构建总览 **4 司机标签体系的搭建及算法****1\. 冷启动**2\. LSTM多标签模型算法 5 货运价格预测6 总结7 部分核心代码8 最后 0 前言 &#x1f5…

TSN工业交换机惊艳亮相第十九届安博会,光路科技展示TSN技术的实际应用

光路科技在第十九届中国国际社会公共安全博览会&#xff08;CPSE安博会&#xff09;上大放异彩&#xff0c;赢得了全球观众的广泛关注。作为展示先进技术和创新解决方案的杰出平台&#xff0c;光路科技无疑成为了此次博览会的焦点之一。 2023CPSE安博会光路科技TSN交换机惊艳亮…

新品发布:信驰达TI CC1352P7双频段多协议模块RF-TI1352P2,支持Matter over Thread

近日&#xff0c;领先的无线物联网通信模块厂商深圳市信驰达科技推出了基于TI CC1352P7 为核心的双频段&#xff08;Sub-1 GHz 和 2.4 GHz&#xff09;多协议无线模块RF-TI1352P2。 RF-TI1352P2模块除了集成负责应用逻辑的高性能 48 MHz ARM Cortex- M4F 主处理器与一个专用于…

AI:48-基于卷积神经网络的气象图像识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌本专栏包含以下学习方向: 机器学习、深度学…

gcc在线升级

提示&#xff1a;本文在线升级需要服务器连接网络 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技…

第5天:基础入门-资产架构amp;端口amp;应用amp;CDNamp;WAFamp;站库分离amp;负载均衡

第5天&#xff1a;基础入门-资产架构&端口&应用&CDN&WAF&站库分离&负载均衡 #知识点&#xff1a;1. 资产架构-端口&目录&插件接口&多站点&多应用 2. 番外安全-域名&服务器本身&服务厂商&管理人员 3. 考虑阻碍-站库分离&am…

【flink】Task 故障恢复详解以及各重启策略适用场景说明

文章目录 一. 重启策略种类&#xff08;Restart Strategies&#xff09;1. Fixed Delay Restart Strategy2. Failure Rate Restart Strategy3. Fallback Restart Strategy4. No Restart Strategy 二. 故障恢复策略&#xff08;Failover Strategies&#xff09;1. &#xff08;全…

Pycharm 关闭项目卡住

1、升级到最新版本 2、清理缓存 全部勾上 3、修改本地索引 设置找到下面这个&#xff0c;改为&#xff1a;不下载&#xff0c;使用本地索引 4、有人说是和插件有关 pycharm v2023.1.2&#xff0c;我是清理缓存加本地索引解决的&#xff0c;之前 Pycharm 弹了一个窗&#xff0…

佳能R6 m2没有样本文件时的设置方法

佳能R6 m2使用了新的DEC标识和小块长度&#xff0c;一般情况下建议使用加载样本的方式进行处理&#xff0c;如果没有样本也可以使用以下方法。 适用程序:CHS零壹视频恢复程序标准版/专业版/高级版/佳能版 解决问题: 底层原始文件会识别出错&#xff0c;导致很多个小碎片。 …

学习笔记三十三:准入控制

ResourceQuota准入控制器 ResourceQuota准入控制器限制cpu、内存、pod、deployment数量限制存储空间大小 LimitRanger准入控制器在limit名称空间创建pod&#xff0c;不指定资源&#xff0c;看看是否会被limitrange规则自动附加其资源限制创建pod&#xff0c;指定cpu请求是100m&…

『Linux升级路』基础开发工具——yum篇

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;Linux &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、Linux下安装软件的方案 &#x1f4d2;1.1源代码安装 &#x1f4d2;1.2r…

【基于HTML5的网页设计及应用】——实现个人简历表格和伪类选择器应用

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

外汇天眼:MAS下令星展银行暂停六个月的非必要活动

新加坡金融管理局&#xff08;MAS&#xff09;已经决定对星展银行有限公司&#xff08;DBS Bank Ltd&#xff09;的非必要IT变更进行为期六个月的暂停&#xff0c;以确保银行集中精力恢复其数字银行服务的弹性。在此期间&#xff0c;星展银行将不被允许开展新的业务&#xff0c…

Nginx代理转发请求POST变GET请求问题

&#x1f680; 注重版权&#xff0c;转载请注明原作者和原文链接 &#x1f96d; 作者&#xff1a;Yuan-Programmer &#x1f34e; 个人博客&#xff1a;https://boke.open-yuan.com &#x1f349; 已经替换了新的域名&#xff0c;总站叫做&#xff1a;OpenYuan开袁网&#xff0…

滤波器及其离散化

原理介绍 令 A aT 一阶低通滤波器&#xff08;离散化&#xff09; - 知乎 (zhihu.com) 【精选】低通滤波器总结_低通滤波器 计算公式 离散_奇妙水果的博客-CSDN博客 MATLAB数值仿真FOC矢量控制_matlab foc模型_奇妙水果的博客-CSDN博客

03、移动零:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

文章目录 1、题目描述1.1 移动所有零至数组末尾1.2 示例 2、解题思路2.1 思路讲解2.2 动画演示&#xff08; 待补充&#xff09;2.3 复杂度分析 3、答案3.1 Java 代码3.2 运行结果 4、视频讲解&#xff08; 待补充&#xff09; 1、题目描述 1.1 移动所有零至数组末尾 给定一个…

nodelist 与 HTMLCollection 的区别

原地址 https://cloud.tencent.com/developer/article/2013289 节点与元素 根据 W3C 的 HTML DOM 标准&#xff0c;HTML 文档中的所有内容都是节点&#xff1a; 整个文档是一个文档节点每个 HTML 元素是元素节点HTML 元素内的文本是文本节点每个 HTML 属性是属性节点注释是注…

Linux安装nodejs指定版本

压缩包官网地址&#xff0c;按需下载 https://nodejs.org/dist/ 一、下载 wget https://nodejs.org/dist/v16.16.0/node-v16.16.0-linux-x64.tar.xz 二、解压 tar -xf node-v16.16.0-linux-x64.tar.xz 三、配置 #配置node /path/to/node-v16.16.0-linux-x64为解压目录 如…

掌控你的Mac性能:System Dashboard Pro,一款专业的系统监视器

作为Mac用户&#xff0c;你是否曾经想要更好地了解你的电脑性能&#xff0c;以便优化其运行&#xff1f;是否想要实时监控系统状态&#xff0c;以便及时发现并解决问题&#xff1f;如果你有这样的需求&#xff0c;那么System Dashboard Pro就是你的不二之选。 System Dashboar…