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,根据实际情况来进行配置。)

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

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

相关文章

有没有寄快递省钱的方法啊,尤其是搬家寄大件?

从大学开始离家&#xff0c;到读研&#xff0c;工作&#xff0c;辗转换了四五个城市了&#xff0c;大大小小搬家十几次&#xff0c;最近才发现有一个宝藏寄快递方法。 个人寄件贵是为什么呢&#xff1f;据我做物流的朋友说一般我们寄快递因为单量少&#xff0c;所以单件价格都…

Windows下Docker安装Kafka3+集群

编写 docker-compose.yaml 主要参照&#xff1a;https://www.cnblogs.com/wangguishe/p/17563274.html version: "3"services:kafka1:image: bitnami/kafka:3.4.1container_name: kafka1environment:- KAFKA_HEAP_OPTS-Xmx1024m -Xms1024m- KAFKA_ENABLE_KRAFTyes- K…

【一】学习TDengine-总结新技术学习的思考

学习TDengine-总结新技术学习的思考 概要 因业务场景需要我们开始接触时序数据库&#xff0c;于是开始根据以往的学习经验着手熟悉这一项新技术&#xff0c;学习也是一种技能&#xff0c;成功的人越容易成功&#xff0c;因为他们掌握了一套成功的方法&#xff0c;这里提到学习经…

windows 线程同步的四种方式总结

一&#xff1a;内核态下的三种同步方式&#xff1a; 一、互斥变量&#xff08;Mutex&#xff09; 互斥对象包含一个使用数量&#xff0c;一个线程ID和一个计数器。其中线程ID用于标识系统中的哪个线程当前拥有互斥对象&#xff0c;计数器用于指明该线程拥有互斥对象的次数。 创…

Vue的学习之旅-part5

Vue的学习之旅-part5 虚拟DOM的原理用JS模拟DOM结构 vue的方法、计算属性、过滤器computed:{} 计算属性computed计算属性的完全体computed计算属性和methods方法的区别&#xff1a;过滤器&#xff1a;filters:{ 多个方法 } Vuex 状态管理模式 前几篇博客: Vue的学习之旅-part1 …

【算法】第二篇 大衍数列

导航 1. 简介2. 数列特征3. 代码演示 1. 简介 大衍数列&#xff0c;来源于《乾坤谱》中对易传“大衍之数五十”的推论。主要用于解释中国传统文化中的太极衍生原理。数列中的每一项&#xff0c;都代表太极衍生过程中&#xff0c;曾经经历过的两仪数量总和。是中华传统文化中隐…

A Study of Network Forensic Investgation in Docker Environments文章翻译

A Study of Network Forensic Investgation in Docker Environments Docker环境下的网络取证研究 摘要 网络罪犯利用越来越多的技术(如虚拟机或基于容器的基础设施)进行恶意活动。 这些虚拟环境的固有动态简化了恶意服务的快速创建,并隐藏了所涉及的系统,这是以前没有的技…

用AI作图,使用这个免费网站,快看我画的大鹏鸟和美女

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

中科院发布大模型想象增强法IAG,无需外部资源,想想就能变强

在人工智能领域&#xff0c;尤其是自然语言处理&#xff08;NLP&#xff09;的子领域——问答系统&#xff08;QA&#xff09;中&#xff0c;知识的获取和利用一直是推动技术进步的核心问题。近年来&#xff0c;大语言模型&#xff08;LLMs&#xff09;在各种任务中展现出了惊人…

风电场智能化转型基于ARM工控机的HDMI数据实时监控显示

全球能源结构不断调整的大背景下&#xff0c;智能电网、太阳能发电、风能发电等清洁能源领域正经历着一场由技术创新引领的深刻变革。在这场变革中&#xff0c;ARM架构的工控机凭借其出色的性能、低功耗及高度可定制化的特点&#xff0c;正在成为能源管理系统的核心组件&#x…

轴向磁通电机应用场景不断扩展 未来市场存在较大开发空间

轴向磁通电机应用场景不断扩展 未来市场存在较大开发空间 根据磁通方向不同&#xff0c;磁通电机分为轴向磁通电机、径向磁通电机两大类&#xff0c;其中轴向磁通电机的磁通方向为轴向&#xff0c;载流导体系径向放置。轴向磁通电机特点在于结构上旋转转子位于定子的侧面&#…

【算法统治世界】动态规划 个人笔记总结

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《数据结构与算法&#xff1a;初学者入门指南》&#x1f4d8;&am…

面试字节被挂了

分享一个面试字节的经历。 1、面试过程 一面&#xff1a;上来就直接"做个题吧"&#xff0c;做完之后&#xff0c;对着简历上一个项目聊&#xff0c;一直聊到最后&#xff0c;还算比较正常。 二面&#xff1a;做自我介绍&#xff0c;花几分钟聊了一个项目&#xff…

数据库入门-----SQL基础知识

目录 &#x1f4d6;前言&#xff1a; &#x1f4d1;SQL概述&&通用语法&#xff1a; &#x1f433;DDL&#xff1a; &#x1f43b;操作数据库&#xff1a; &#x1f41e;数据类型&#xff1a; &#x1f989;操作表&#xff1a; &#x1f9a6;DML: 语法规则&#x…

浅析安全传输协议HTTPS之“S”

当前互联网&#xff0c;在各大浏览器厂商和CA厂商的推动下&#xff0c;掀起了一股HTTPS应用浪潮。为了让大家更好的了解HTTPS&#xff0c;本文给大家介绍关于HTTPS 中的S一个整体的认识。从其产生的历史背景、设计目标说起&#xff0c;到分析其协议设计结构、交互流程是如何实现…

R语言数据操纵:常用函数

目录 处理循环的函数 lapply函数 apply函数 mapply函数 tapply函数 split函数 排序的函数 sort函数与order函数 总结数据信息的函数 head函数与tail函数 summary函数 str函数 table函数 any函数 all函数 xtab函数 object.size函数 这篇文章主要介绍R语言中处理…

HarmonyOS 开发-一镜到底“页面转场”动画

介绍 本方案做的是页面点击卡片跳转到详情预览的转场动画效果 效果图预览 使用说明 点击首页卡片跳转到详情页&#xff0c;再点击进入路由页面按钮&#xff0c;进入新的路由页面 实现思路 首页使用了一种视觉上看起来像是组件的转场动画&#xff0c;这种转场动画通常是通过…

swiftui macOS实现加载本地html文件

import SwiftUI import WebKitstruct ContentView: View {var body: some View {VStack {Text("测试")HTMLView(htmlFileName: "localfile") // 假设你的本地 HTML 文件名为 index.html.frame(minWidth: 100, minHeight: 100) // 设置 HTMLView 的最小尺寸…

RabbitMQ-延迟队列的使用

目录 一、使用场景 二、第一种方式&#xff1a;创建具有超时功能且绑定死信交换机的消息队列 三、第二种方式&#xff1a;创建通用延时消息 四、第三种方式&#xff1a;使用rabbitmq的延时队列插件&#xff0c;实现同一个队列中有多个不同超时时间的消息&#xff0c;并按时间…

春秋之境28512

题目说该CMS的/single.php路径下&#xff0c;id参数存在一个SQL注入漏洞。访问看一下随便点一个图片。 发现了注入点?id 那么开始查看闭合符一个 就报错了 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for th…