当前位置: 首页 > news >正文

雪花算法(Snowflake Algorithm)

雪花算法(Snowflake Algorithm)是一种分布式唯一ID生成算法,主要用于生成全球唯一的ID,广泛应用于分布式系统中,例如在数据库中作为主键。这个算法最初由Twitter提出,并且被广泛使用在很多大规模系统中。有以下特性:

唯一性: 确保生成的每一个ID都是全球唯一的,避免冲突。

时间有序性: 根据生成ID的时间戳,使得ID按时间顺序排列,方便进行时间排序和处理。

高效性: 在高并发环境下能够快速生成ID,通常在每毫秒内能够生成多个ID。

雪花算法的核心思想是将ID分解成几个部分,以确保生成的ID具有唯一性,同时又能保持一定的顺序性。它的ID结构一般包括以下几个部分:

时间戳(Timestamp): 用于表示当前时间的毫秒数。通常,时间戳占用最前面的一部分位数,这样可以保证ID在时间上有序。

数据中心ID(Data Center ID): 用于标识不同的数据中心,确保在不同的数据中心生成的ID不会冲突。

工作机器ID(Worker ID): 用于标识同一数据中心下的不同工作机器,确保同一数据中心内的不同机器生成的ID也不会冲突。

序列号(Sequence Number): 用于在同一毫秒内生成多个ID,以保证同一时间戳下生成的ID仍然唯一。

雪花算法通常生成的ID长度是64位,具体结构可能有所不同,但一个常见的结构是:

1位符号位(通常固定为0)

41位时间戳(表示毫秒数,一般支持约69年的时间范围)

10位数据中心ID和工作机器ID(分为5位数据中心ID和5位工作机器ID,支持最多1024个数据中心,每个数据中心支持最多1024台机器)

12位序列号(支持每毫秒生成4096个不同的ID)

这种设计可以保证生成的ID是全局唯一的,同时还能保持一定的顺序性,适用于高并发的分布式系统。

工作原理

生成ID时,算法会获取当前时间戳。

结合数据中心ID、工作机器ID以及序列号生成唯一ID。

如果在同一毫秒内生成多个ID,算法会增加序列号以确保唯一性。

在时间戳变化时,算法会重置序列号,并更新ID的时间戳部分。

优点有:

全局唯一性:‌能够在分布式系统中确保生成的ID全局唯一。‌

趋势递增性:‌生成的ID基本呈趋势递增,‌有利于数据库性能优化。‌

灵活性:‌可以根据业务特性灵活分配bit位。‌

高性能:‌不依赖数据库等第三方系统,‌以服务方式部署,‌生成ID的性能高。‌

缺陷有:‌

时钟回退问题:雪花算法依赖于系统时间戳来生成唯一 ID。如果系统时间被设置回过去,可能会导致生成重复的 ID 或者抛出异常。这是使用雪花算法时需要特别注意的问题,因为时钟回退会直接影响到 ID 的唯一性和生成稳定性。

时间戳依赖:雪花算法的 ID 生成是基于系统时间戳的。如果系统时间出现问题(如时钟漂移或系统时间错误),则会影响 ID 的生成。这使得系统时间的准确性和稳定性变得至关重要。

数据中心 ID 和工作机器 ID 的管理:需要合理分配和管理数据中心 ID 和工作机器 ID,以避免冲突。在大规模分布式系统中,如何分配和协调这些 ID 是一个挑战,错误的配置可能导致 ID 冲突或生成不一致。

高并发处理复杂性:在高并发场景下,雪花算法通过序列号来处理生成的 ID,但在极高负载情况下,仍然需要精细管理性能和同步问题。高并发可能导致性能瓶颈或增加实现的复杂性。

代码实现

public class SnowflakeIdGenerator {// 起始时间戳 (2020-01-01 00:00:00)private static final long START_TIMESTAMP = 1577836800000L;// 工作机器 ID 位数private static final long WORKER_ID_BITS = 5L;// 数据中心 ID 位数private static final long DATA_CENTER_ID_BITS = 5L;// 序列号位数private static final long SEQUENCE_BITS = 12L;// 工作机器 ID 最大值private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);// 数据中心 ID 最大值private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);// 序列号最大值private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);// 工作机器 ID 和数据中心 IDprivate final long workerId;private final long dataCenterId;// 序列号和上次时间戳private long sequence = 0L;private long lastTimestamp = -1L;/*** 构造函数,初始化工作机器 ID 和数据中心 ID。* * @param workerId 工作机器 ID* @param dataCenterId 数据中心 ID* @throws IllegalArgumentException 如果 workerId 或 dataCenterId 超出范围*/public SnowflakeIdGenerator(long workerId, long dataCenterId) {if (workerId > MAX_WORKER_ID || workerId < 0) {throw new IllegalArgumentException("Worker ID out of range");}if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {throw new IllegalArgumentException("Data Center ID out of range");}this.workerId = workerId;this.dataCenterId = dataCenterId;}/*** 生成下一个唯一的 ID。* * @return 生成的 ID*/public synchronized long nextId() {long timestamp = currentTimestamp();// 检查时钟是否回退if (timestamp < lastTimestamp) {throw new RuntimeException("Clock moved backwards. Refusing to generate id");}// 当前时间戳与上次时间戳相同,处理序列号溢出if (timestamp == lastTimestamp) {sequence = (sequence + 1) & SEQUENCE_MASK;if (sequence == 0) {timestamp = waitNextMillis(lastTimestamp);}} else {// 当前时间戳不同,重置序列号sequence = 0L;}lastTimestamp = timestamp;// 生成唯一 IDreturn ((timestamp - START_TIMESTAMP) << (WORKER_ID_BITS + DATA_CENTER_ID_BITS + SEQUENCE_BITS)) |(dataCenterId << (WORKER_ID_BITS + SEQUENCE_BITS)) |(workerId << SEQUENCE_BITS) |sequence;}/*** 等待直到下一个毫秒。* * @param lastTimestamp 上次生成 ID 的时间戳* @return 当前时间戳*/private long waitNextMillis(long lastTimestamp) {long timestamp = currentTimestamp();while (timestamp <= lastTimestamp) {timestamp = currentTimestamp();}return timestamp;}/*** 获取当前时间戳。* * @return 当前时间戳(毫秒)*/private long currentTimestamp() {return System.currentTimeMillis();}/*** 测试生成 ID。* * @param args 命令行参数*/public static void main(String[] args) {SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);for (int i = 0; i < 10; i++) {System.out.println(idGenerator.nextId());}}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • C/C++字符串函数
  • LVS (Linux virual server)
  • 【ARM】ARM Cortex 处理器详细讲解
  • Upload 上传图标不显示
  • 【C#】StringComparer
  • 智能电话机器人的优势与挑战
  • 在mac上通过 MySQL 安装包安装 MySQL 之后,终端执行 mysql 命令报错 command not found: mysql
  • Pytorch-张量的创建
  • 电脑装机-热插拔
  • P1012 [NOIP1998 提高组] 拼数
  • java基础学习笔记1
  • 50 mysql 的 “where 1 = 1“ 的优化处理
  • 开启智能能效管理:4G 智能计量控制插座的协议对接与私有化部署
  • 36、Python之面向对象:容器类协议与collections.abc
  • Android进阶之路 - app后台切回前台触发超时保护退出登录
  • 【css3】浏览器内核及其兼容性
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • Debian下无root权限使用Python访问Oracle
  • Elasticsearch 参考指南(升级前重新索引)
  • gf框架之分页模块(五) - 自定义分页
  • LeetCode18.四数之和 JavaScript
  • Mysql5.6主从复制
  • mysql外键的使用
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • React Native移动开发实战-3-实现页面间的数据传递
  • Sublime Text 2/3 绑定Eclipse快捷键
  • 当SetTimeout遇到了字符串
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 扑朔迷离的属性和特性【彻底弄清】
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • gunicorn工作原理
  • HanLP分词命名实体提取详解
  • Nginx实现动静分离
  • 测评:对于写作的人来说,Markdown是你最好的朋友 ...
  • 如何在招聘中考核.NET架构师
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • ​Linux·i2c驱动架构​
  • # SpringBoot 如何让指定的Bean先加载
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • #在 README.md 中生成项目目录结构
  • (11)MSP430F5529 定时器B
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (30)数组元素和与数字和的绝对差
  • (Oracle)SQL优化技巧(一):分页查询
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (十八)Flink CEP 详解
  • (算法)前K大的和
  • (算法设计与分析)第一章算法概述-习题
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (转) 深度模型优化性能 调参