Twitter的分布式自增ID雪花算法snowflake

news/2025/2/14 5:24:05/

Twitter的分布式自增ID雪花算法snowflake

  • 一、Twitter的分布式自增ID雪花算法snowflake
  • endl

一、Twitter的分布式自增ID雪花算法snowflake

/*** Twitter的分布式自增ID雪花算法snowflake* SnowFlake的结构如下(每部分用-分开):* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。* 41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号* 加起来刚好64位,为一个Long型。* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高。* 经测试,SnowFlake每秒能够产生26万ID左右。*/
public class SnowFlake {// 因为二进制里第一个 bit 为如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0/*** 起始的时间戳*/private static final long START_STMP = 1480166465631L;/*** 每一部分占用的位数*/private static final long SEQUENCE_BIT = 12; // 序列号占用的位数private static final long MACHINE_BIT = 5; // 机器标识占用的位数private static final long DATACENTER_BIT = 5; // 数据中心占用的位数/*** 每一部分的最大值*/// 这个是一个意思,就是5 bit最多只能有31个数字,机房id最多只能是32以内private static final long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);// 这个是二进制运算,就是5 bit最多只能有31个数字,也就是说机器id最多只能是32以内private static final long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);// 每毫秒内产生的id数 2 的 12次方private static final long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);/*** 每一部分向左的位移*/private static final long MACHINE_LEFT = SEQUENCE_BIT;private static final long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;private static final long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;// 机房ID 2进制5位  32位减掉1位 31个private long datacenterId; // 数据中心、机房ID// 机器ID  2进制5位  32位减掉1位 31个private long machineId; // 机器标识// 代表一毫秒内生成的多个id的最新序号  12位 4096 -1 = 4095 个private long sequence = 0L; // 序列号// 记录产生时间毫秒数,判断是否是同1毫秒private long lastTimestamp = -1L; // 上一次时间戳public SnowFlake(long datacenterId, long machineId) {// 检查机房id和机器id是否超过31 不能小于0if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");}if (machineId > MAX_MACHINE_NUM || machineId < 0) {throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");}this.datacenterId = datacenterId;this.machineId = machineId;}/*** 这个是核心方法,通过调用nextId()方法,让当前这台机器上的snowflake算法程序生成一个全局唯一的id*/public synchronized long nextId() {long currentTimestamp = getNewsTimestamp();if (currentTimestamp < lastTimestamp) {throw new RuntimeException("Clock moved backwards.  Refusing to generate id");}// 下面是说假设在同一个毫秒内,又发送了一个请求生成一个id// 这个时候就得把seqence序号给递增1,最多就是4096if (currentTimestamp == lastTimestamp) {// 这个意思是说一个毫秒内最多只能有4096个数字,无论你传递多少进来,// 这个位运算保证始终就是在4096这个范围内,避免你自己传递个sequence超过了4096这个范围sequence = (sequence + 1) & MAX_SEQUENCE;// 当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生IDif (sequence == 0L) {currentTimestamp = getNextMill();}} else {// 不同毫秒内,序列号置为0sequence = 0L;}lastTimestamp = currentTimestamp;// 最核心的二进制位运算操作,生成一个64bit的id// 先将当前时间戳左移,放到41 bit那儿;将机房id左移放到5 bit那儿;将机器id左移放到5 bit那儿;将序号放最后12 bit// 最后拼接起来成一个64 bit的二进制数字,转换成10进制就是个long型return (currentTimestamp - START_STMP) << TIMESTMP_LEFT // 时间戳部分| datacenterId << DATACENTER_LEFT // 数据中心部分| machineId << MACHINE_LEFT // 机器标识部分| sequence; // 序列号部分}/*** 阻塞到下一个毫秒,直到获得新的时间戳* lastTimestamp 上次生成ID的时间截* @return 当前时间戳*/private long getNextMill() {long mill = getNewsTimestamp();while (mill <= lastTimestamp) {mill = getNewsTimestamp();}return mill;}/*** 返回以毫秒为单位的当前时间* @return 当前时间(毫秒)*/private long getNewsTimestamp() {return System.currentTimeMillis();}public static void main(String[] args) {SnowFlake snowFlake = new SnowFlake(2, 3);long start = System.currentTimeMillis();for (int i = 0; i < 100000; i++) {System.out.println("当前生成的有序数字串 : " + (snowFlake.nextId()));}System.out.println("总耗时 : " + (System.currentTimeMillis() - start));System.out.println("MAX_MACHINE_NUM : " + MAX_MACHINE_NUM);System.out.println("MAX_DATACENTER_NUM : " + MAX_DATACENTER_NUM);System.out.println("MAX_SEQUENCE : " + MAX_SEQUENCE);}
}

endl


http://www.ppmy.cn/news/1379262.html

相关文章

QT:用opencv的KNN识别图片中的LED数字(一)

前言 一款功能测试的软件demo,使用了QT作为界面,主要使用了opencv的KNN识别,使用gstreamer作为管道,用来打开图片。后期会写一篇打开摄像头实时识别的文章。 (正在写,未完成,稍候) 效果一预览: 效果二预览: 效果三预览: 正在写。。。 设计思路 1. 软件UI设计 2. …

性能测试入门:做一次简单的性能测试

当前&#xff0c;性能测试已经是一名软件测试工程师必须要了解&#xff0c;甚至熟练使用的一项技能了&#xff0c;在工作时可能每次发版都要跑一遍性能&#xff0c;跑一遍自动化。性能测试入门容易&#xff0c;深入则需要太多的知识量&#xff0c;今天这篇文章给大家带来&#…

初步了解linux基本命令

1.删除虚拟机&#xff1a; 右键虚拟机的标签&#xff0c;选择管理&#xff0c;选择从磁盘中删除 2.内存、硬盘 机硬硬盘—容量大—读写慢—价格便宜 固态硬盘—容量小—读写快—价格昂贵 电源&#xff1a;在生产环境中&#xff0c;服务器都是使用AB双电源线路 3.服务器 服…

中国广电的独特优势:与三大运营商相比的亮点

2023年&#xff0c;中国广电正式上市了&#xff0c;发出了第一批号段192的号码&#xff0c;然而值得大家了解的是&#xff1a;在中国的通信市场中&#xff0c;中国移动、中国联通和中国电信长期以来占据主导地位。然而&#xff0c;随着中国广电的加入&#xff0c;市场格局正在发…

堆宝塔(Python)

作者 陈越 单位 浙江大学 堆宝塔游戏是让小朋友根据抓到的彩虹圈的直径大小&#xff0c;按照从大到小的顺序堆起宝塔。但彩虹圈不一定是按照直径的大小顺序抓到的。聪明宝宝采取的策略如下&#xff1a; 首先准备两根柱子&#xff0c;一根 A 柱串宝塔&#xff0c;一根 B 柱用于…

吴恩达机器学习笔记十六 如何debug一个学习算法 模型评估 模型选择和训练 交叉验证测试集

如果算法预测出的结果不太好&#xff0c;可以考虑以下几个方面&#xff1a; 获得更多的训练样本 采用更少的特征 尝试获取更多的特征 增加多项式特征 增大或减小 λ 模型评估(evaluate model) 例如房价预测&#xff0c;用五个数据训练出的模型能很好的拟合这几个数据&am…

独家原创!基于遗传算法的综合能源系统优化调度程序代码!

适用平台&#xff1a;MatlabYalmipCplex 程序以风能、光伏、火电以及储能作为主要设备建立了综合能源优化调度模型&#xff0c;提出了多种群变异交叉概率自适应变化遗传算法对模型进行求解&#xff0c;该算法可以根据遗传算法的运行中的表现来调整交叉和变异概率&#xff0c;具…

Python中的并发编程:多线程与多进程的比较【第124篇—多线程与多进程的比较】

Python中的并发编程&#xff1a;多线程与多进程的比较 在Python编程领域中&#xff0c;处理并发任务是提高程序性能的关键之一。本文将探讨Python中两种常见的并发编程方式&#xff1a;多线程和多进程&#xff0c;并比较它们的优劣之处。通过代码实例和详细的解析&#xff0c;…