java中使用雪花算法(Snowflake)为分布式系统生成全局唯一ID

(全局唯一ID的解决方案有很多种,这里主要是介绍和学习Snowflake算法)

什么是雪花算法(Snowflake)

雪花算法(Snowflake Algorithm)是由Twitter公司在2010年左右提出的一种分布式ID生成算法,主要用于生成全局唯一且趋势递增的ID。这种算法生成的ID是一个64位的长整型数字,具有很高的性能与扩展性,特别适合于分布式环境下的主键生成场景,比如数据库表主键、消息队列的Message ID等。

实现原理

Snowflake算法的原理主要体现在它生成64位ID的结构上,主要划分为如下几个部分:

0 | 00000000000000000000000000000000000000000 | 00000 | 00000 | 000000000000

  • 1bit-符号位:

        第1位通常固定为0,表示生成的ID都是正数。

  • 41bit-时间戳部分:

        从第2位到第42位(共41位)存储时间戳信息,精确到毫秒级别。时间戳可以是自定义的一个起始时间点(如Twitter使用的是2010-11-04的某一时刻),这样可以通过比较ID中的时间戳部分来判断事件发生的先后顺序。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69。

  • 10bit-工作机器ID(5bit数据中心ID+5bit机器ID):

        从第43位到第52位(共10位)存储工作机器ID或者数据中心ID。这部分可以进一步细分为两部分,例如前5位标识数据中心ID,后5位标识工作节点ID。这样可以支持32(0~31)个数据中心以及每个数据中心内部的32(0~31)个工作节点,足够覆盖大规模分布式系统的节点标识。

  • 12bit-序列号部分:

        从第53位到第64位(共12位)存储同一节点同一毫秒内生成的序列号,这意味着同一个节点在同毫秒内可以生成最多4096个不同的ID(2^12)。

当生成ID时,首先获取当前时间戳,然后加上工作节点ID以及序列号。如果在同一毫秒内有新的请求,则序列号加1。若序列号达到最大值,则等待下一毫秒再进行分配,从而确保在同一节点内生成的ID是唯一的

雪花算法的优缺点

优点:

  1. 全局唯一性:雪花算法生成的ID是全局唯一的,这在分布式系统中非常重要,可以避免因ID冲突而导致的数据不一致问题。

  2. 递增有序:由于ID中包含时间戳部分,所以生成的ID是递增有序的。这有助于数据库插入性能的优化,因为有序的ID可以减少数据库的页分裂,提高写入效率。

  3. 灵活性:雪花算法允许自定义配置工作机器ID和数据中心ID的位数,可以根据实际部署环境调整这些配置,以支持不同规模的分布式系统。

  4. 高效性:算法本身实现简单,生成ID的速度快,能够满足高并发场景下的需求。

缺点:

  1. 时钟依赖:雪花算法依赖于系统时钟来生成时间戳部分。如果系统时钟出现回拨或漂移,可能会导致生成的ID不唯一或有序性受到破坏。虽然可以通过一些机制来处理时钟回拨问题,但时钟漂移仍然是一个潜在的风险。

  2. 机器ID冲突:如果部署的工作节点数量超过了算法中定义的机器ID位数所能表示的范围,就会发生机器ID冲突。这需要在设计系统时预先规划好机器ID的分配和管理。

  3. 缺乏安全性:雪花算法生成的ID本身并不包含加密或签名信息,因此容易受到恶意篡改。如果ID的安全性要求较高,需要在生成ID后添加额外的加密或签名措施。

  4. 扩展性限制:由于雪花算法的ID结构是固定的,因此在某些情况下可能会受到扩展性的限制。例如,如果未来需要添加更多的元数据到ID中,或者需要支持更大的分布式系统规模,可能需要重新设计ID生成算法。

因此,为了更全面地解决雪花算法的缺陷问题,可能需要采取额外的措施,例如:

  • 增强时钟同步:使用NTP(Network Time Protocol)或其他时钟同步机制来确保各个节点之间的时钟尽可能准确同步。

  • 增加机器ID的灵活性:设计一种更灵活的方式来分配和管理机器ID,以便支持更多的工作节点和数据中心。

  • 安全性考虑:对生成的ID进行加密或签名,以防止恶意篡改。

综上所述,雪花算法在分布式系统中具有广泛的应用价值,其全局唯一性和递增有序性使得它成为生成唯一ID的优选方案之一。然而,在使用雪花算法时也需要注意其潜在的缺点,并根据实际需求进行配置和优化。

Snowflake算法生成ID的Java代码示例

以下是Snowflake算法的一个java简化版实现:

public class SnowflakeIdWorker {  
    // 起始的时间戳(自定义,例如系统上线时间)  
    private final long twepoch = 1288834974657L;  
  
    // 机器id所占的位数  
    private final long workerIdBits = 5L;  
  
    // 数据标识id所占的位数  
    private final long datacenterIdBits = 5L;  
  
    // 最大机器ID  
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);  
  
    // 最大数据标识ID  
    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=4095)  
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);  
  
    // 上次生成ID的时间截  
    private long lastTimestamp = -1L;  
  
    // 序列号  
    private long sequence = 0L;  
  
    // 工作机器ID  
    private final long workerId;  
  
    // 数据中心ID  
    private final long datacenterId;  
  
    public SnowflakeIdWorker(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  
    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 {  
            // 时间戳改变,序列号重置为0  
            sequence = 0L;  
        }  
  
        // 更新最后的时间戳  
        lastTimestamp = timestamp;  
  
        // 移位并通过或运算拼到一起组成64位的ID  
        return ((timestamp - twepoch) << timestampLeftShift) |  
               (datacenterId << datacenterIdShift) |  
               (workerId << workerIdShift) |  
               sequence;  
    }  
  
    // 获取当前时间戳  
    protected long timeGen() {  
        return System.currentTimeMillis();  
    }  
  
    // 等待下一个毫秒  
    protected long tilNextMillis(long lastTimestamp) {  
        long timestamp = timeGen();  
        while (timestamp <= lastTimestamp) {  
            timestamp = timeGen();  
        }  
        return timestamp;  
    }

    public static void main(String[] args) {  
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 1);  
        for (int i = 0; i < 5; i++) {  
            long id = idWorker.nextId();  
            System.out.println(Long.toBinaryString(id));  
            System.out.println(id);  
        }  
    }  
}

代码输出:

这段代码实现了雪花算法的核心逻辑。在nextId()方法中,它首先获取当前时间戳,然后检查时间戳是否小于上一次生成ID时的时间戳,如果是,则抛出异常,因为这意味着系统时钟回退,可能会导致ID生成出现混乱。如果时间戳相同,则序列号自增,并检查是否溢出,如果溢出则等待下一个毫秒。如果时间戳不同,则重置序列号。最后,将时间戳、数据中心ID、机器ID和序列号按照各自的偏移量左移,然后进行位或运算,组合成一个64位的ID。

(注:关于数据中心ID、机器ID,根据实际情况来进行配置。)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一起学习python——基础篇(10)

前言&#xff0c;Python 是一种面向对象的编程语言。以前大学读书的时候经常开玩笑说的一句话“如果没有对象&#xff0c;就new一个”。起因就是编程老师上课时经常说一句“首先&#xff0c;我们new一个对象”。 今天讲一下python的类和对象。 类是什么&#xff1f;它是一种用…

应用商店备案登记流程解析

引言&#xff1a; 随着智能手机的普及和移动互联网的发展&#xff0c;移动应用程序&#xff08;App&#xff09;已成为人们日常生活中不可或缺的一部分。在开发一个App之后&#xff0c;开发者需要将其上传到应用商店进行审核和上架。然而&#xff0c;在上架之前&#xff0c;开…

项目管理-Jiar Software

文章目录 前言Jira 中的关键词或术语功能应用场景优势 总结 前言 Jira Software 是由澳大利亚公司 Atlassian 开发的一款领先的敏捷项目管理工具&#xff0c;广泛应用于软件开发团队&#xff0c;以支持复杂的项目管理需求。以下是关于 Jira Software 的详细介绍&#xff0c;包…

银行内部控制管理系统应用架构最全介绍

内部控制管理系统 实物资产管理系统 依据《企业内部控制应用指引第 8 号——资产管理》&#xff0c;金融企业应当建立实物资产管理的岗位责任制度&#xff0c;对实物资产的验收入库、领用、发出、盘点、保管及处置等关键环节进行控制&#xff0c;防止各种实物资产被盗、毁损和…

mac中创建的证书提示是无效或者是证书不受信任的解决办法

mac中创建的证书提示是无效或者是证书不受信任的解决办法 原因&#xff1a; &#xff08;1&#xff09;可能是由于自己的误删除将Apple worldwide Developer Relatioans Certification Authority删除掉了 (2) 由于签发的认证的证书到期了 &#xff08;3&#xff09;其它未知原…

【趣味学算法】14_梅森素数

注&#xff1a; 本系列仅为个人学习笔记&#xff0c;学习内容为《算法小讲堂》&#xff08;视频传送门&#xff09;&#xff0c;通俗易懂适合编程入门小白&#xff0c;需要具备python语言基础&#xff0c;本人小白&#xff0c;如内容有误感谢您的批评指正 梅森数&#xff08;Me…

ML Kit:通过Mendix 集成人脸识别算法

预训练模型是一种已经使用训练数据集进行训练并包含执行模型所需所有参数的机器学习模型。这类模型常用于计算机视觉领域&#xff0c;比如可以在Mendix Studio Pro中导入ONNX模型后&#xff0c;可以在微流程中执行该模型。 本文讲述如何在Mendix应用程序中集成特定的人脸检测模…

短视频培训要多少钱?

在互联网时代&#xff0c;短视频已经成为一种流行的传播方式&#xff0c;不仅可以记录生活的美好瞬间&#xff0c;还可以作为一种职业技能&#xff0c;帮助个人或企业实现品牌推广和商业变现。因此&#xff0c;越来越多的人开始关注短视频制作培训&#xff0c;希望通过专业的学…

SQL语言自用(持续更新)(带例子)

目录 基础知识数据定义数据查询单表查询连接查询嵌套查询集合运算 实验例子数据定义数据查询单表查询查询的目标表达式为所有列、指定的列或指定的列的运算三种不同。使用DISTINCT保留字消除重复行。对查询结果排序和分组。集合分组使用集函数进行各项统计。 连接查询笛卡儿连接…

【QT入门】 Qt自定义控件与样式设计之QComboBox样式表介绍

往期回顾 【QT入门】 Qt自定义控件与样式设计之QLineEdit的qss使用-CSDN博客 【QT入门】Qt自定义控件与样式设计之QPushButton常用qss-CSDN博客 【QT入门】 Qt自定义控件与样式设计之QPushButton实现鼠标悬浮按钮弹出对话框-CSDN博客 【QT入门】 Qt自定义控件与样式设计之QComb…

LabVIEW和2D激光扫描的受电弓滑板磨耗精确测量

LabVIEW和2D激光扫描的受电弓滑板磨耗精确测量 在电气化铁路运输中&#xff0c;受电弓滑板的健康状况对于保障列车安全行驶至关重要。受电弓滑板作为连接电网与列车的直接介质&#xff0c;其磨损情况直接影响到电能的有效传输及列车的稳定运行。精确、快速测量受电弓滑板磨损情…

天池医疗AI大赛[第一季] Rank5解决方案

一、赛题说明 数据格式 本次大赛数据集包含数千份高危患者的低剂量肺部CT影像&#xff08;mhd格式&#xff09;数据&#xff0c;每个影像包含一系列胸腔的多个轴向切片。每个影像包含的切片数量会随着扫描机器、扫描层厚和患者的不同而有差异。原始图像为三维图像。这个三维图…

力扣经典150题(1)

文章目录 6.Z字形变换82.删除排序链表中的重复元素||61.旋转链表100.相同的树 6.Z字形变换 将一个给定字符串 s 根据给定的行数 numRows &#xff0c;以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 “PAYPALISHIRING” 行数为 3 时&#xff0c;排列如下&#xff1…

【讲解如何OpenCV入门】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

数据库之DQL操作(数据查询语言)

DQL英文全称是Data Query Language(数据查询语言)&#xff0c;数据查询语言&#xff0c;用来查询数据库中表的记录。查询关键字: SELECT。 本节介绍以下表为例&#xff1a; create table emp(id int comment 编号&#xff0c;workno varchar(10) comment 工号&#xff0c;nam…

基于SpringBoot+MyBatis-Plus的代码生成器

一、功能说明 数据源管理&#xff1a;实现多个数据库的表代码生成表管理&#xff1a;从数据源导入表&#xff0c;配置表和字段默认配置&#xff1a;配置项目默认信息&#xff0c;配置字段数据类型映射操作日志 功能截图 二、代码实现 基于velocity-engine模板代码生成 pack…

工单管理系统设计方案,工单系统的流程

工单管理系统是一种用于管理和跟踪工作流程的软件系统。它可以帮助企业和组织更好地分配任务、优化工作流程、提高生产效率和客户满意度。下面是一个基本的工单管理系统设计方案&#xff1a;需求分析  在设计工单管理系统之前&#xff0c;需要进行需求分析&#xff0c;确定系…

三极管原理特性介绍,课堂上可不这么讲!

原文来自微信公众号&#xff1a;工程师看海&#xff0c;与我联系&#xff1a;chunhou0820 看海原创视频教程&#xff1a;《运放秘籍》 大家好&#xff0c;我是工程师看海&#xff0c;原创文章欢迎点赞分享&#xff01; 今天介绍下三极管的特性&#xff0c;清晰易懂&#xff0c…

C语言进阶课程学习记录-第27课 - 数组的本质分析

C语言进阶课程学习记录-第27课 - 数组的本质分析 数组实验-数组元素个数的指定实验-数组地址与数组首元素地址实验-指针与数组地址的区别小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 数组 实验-数…

Ubuntu 22上安装Anaconda3。下载、安装、验证详细教程

在Ubuntu 22上安装Anaconda3&#xff0c;你可以遵循以下步骤&#xff1a; 更新系统存储库&#xff1a; 打开终端并运行以下命令来更新系统存储库&#xff1a; sudo apt update安装curl包&#xff1a; 下载Anaconda安装脚本通常需要使用curl工具。如果系统中没有安装curl&#x…