聊聊雪花算法?

news2025/1/19 14:13:56

随便聊聊

哈喽,大家好,最近换了份工作,虽然后端技术栈是老了点,但是呢,这边的前端技术确是现在市面上最新的那一套技术:Vue3+Vite+TSX+Pina+Element-Plus+NativeUI。我本人主要是学后端的,确被拉去做前端。唉(人生无常,大肠包小肠!)

但是呢,最近经常听到同事们一直在聊雪花算法,下面来,我们来聊聊雪花算法,是的

分布式ID

聊之前先说一下什么是分布式ID,抛砖引玉

假设现在有一个订单系统被部署在了A、B两个节点上,那么如何在这两个节点上各自生成订单ID,且ID值不能重复呢?

即在分布式系统中,如何在各个不同的服务器上产生唯一的ID值?

通常有以下三种方案:

  • 利用数据库的自增特性,不同节点直接使用相同数据库的自增ID;
  • 利用UUID算法产生ID值;
  • 使用雪花算法产生ID值

虽然Java提供了对UUID的支持,使用UUID.randomUUID()即可,但是由于UUID是一串随机的36位字符串,由32个数字和字母混合的字符串和4个“-”组成,长度过长且业务可读性差,无法有序递增,所以一般不用,更多使用的是雪花算法

由来

为什么叫雪花算法?

雪花算法的由来有两种说法:

  • 第一种,Twitter使用scala语言开源了一种分布式id生成算法—SnowFlake算法,被翻译成了雪花算法;
  • 第二种,因为自然界中并不存在两片完全一样的雪花的,每一片雪花都拥有自己漂亮独特的形状、独一无二。雪花算法也表示生成的ID如雪花般独一无二。

组成

雪花算法生成的ID到底长啥样?

雪花算法生成的ID是一个64bit的long型的数字且按时间趋势递增。大致由首位无效符、时间戳差值、机器编码,序列号四部分组成。
在这里插入图片描述
如图:

  • 首位无效符:第一个bit作为符号位,因为我们生成的都是正数,所以第一个bit统一都是0;
  • 时间戳:占用41bit,精确到毫秒。41位最好可以表示2^41-1毫秒,转换成单位年为69年;
  • 机器编码:占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,最多可以容纳1024个节点;
  • 序列号:占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096个ID

代码

/**
 * 雪花算法
 * @author Fang Ruichuan
 * @date 2022-11-28 21:24
 */
public class SnowFlake {
    private long workerId;
    private long dataCenterId;

    // 每毫秒生产的序列号之从0开始递增
    private long sequence = 0L;


    // 1288834974657L是1970-01-01 00:00:00到2010年11月04日01:42:54所经过的毫秒数;
    // 因为现在二十一世纪的某一时刻减去1288834974657L的值,正好在2^41内。
    // 因此1288834974657L实际上就是为了让时间戳正好在2^41内而凑出来的。
    // 简言之,1288834974657L(即1970-01-01 00:00:00),就是在计算时间戳时用到的“起始时间”。
    private long twePoch = 1288834974657L;

    private long workerIdBits = 5L;
    private long datacenterIdBits = 5L;
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private long sequenceBits = 12L;

    private long workerIdShift = sequenceBits;
    private long datacenterIdShift = sequenceBits + workerIdBits;
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long lastTimestamp = -1L;

    public SnowFlake(long datacenterId, long workerId) {
        if ((datacenterId > maxDatacenterId || datacenterId < 0)
            || (workerId > maxWorkerId || workerId < 0)) {
            throw new IllegalArgumentException("datacenterId/workerId值非法");
        }
        this.dataCenterId = datacenterId;
        this.workerId = workerId;
    }

    // 通过SnowFlake生成id的核心算法
    public synchronized long nextId() {
        long timestamp = Clock.systemUTC().millis();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException("时间戳值非法");
        }
        // 如果此次生成id的时间戳,与上次的时间戳相同,就通过机器码和序列号区
        // 分id值(机器码已通过构造方法传入)
        if (lastTimestamp == timestamp) {
             /*
                下一条语句的作用是:通过位运算保证sequence不会超出序列号所能容纳的最大值。
                例如,本程序产生的12位sequence值依次是:1、2、3、4、...、4094、4095
                (4095是2的12次方的最大值,也是本sequence的最大值)
                那么此时如果再增加一个sequence值(即sequence + 1),下条语句就会
                使sequence恢复到0。
                即如果sequence==0,就表示sequence已满。
             */
            sequence = (sequence + 1) & sequenceMask;
            // 如果sequencce已满,就无法再通过sequence区分id值:因此需要切换到
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 如果此次生成id的时间戳,与上次的时间戳不同,就已经可以根据时间戳区分id值
            sequence = 0L;
        }
        // 更新最近一次次生成id的时间戳
        lastTimestamp = timestamp;

        /**
         *
         * 假设此刻的值是(二进制表示):
         *                 41位时间戳的值是:00101011110101011101011101010101111101011
         *                 5位datacenterId(机器码的前5位)的值是:01101
         *                 5位workerId(机器码的后5位)的值是:11001
         *                 sequence的值是:01001
         *             那么最终生成的id值,就需要:
         *                 1.将41位时间戳左移动22位(即移动到snowflake值中时间戳应该出现的位置);
         *                 2.将5位datacenterId向左移动17位,并将5位workerId向左移动12位
         *                 (即移动到snowflake值中机器码应该出现的位置);
         *                 3.sequence本来就在最低位,因此不需要移动。
         *             以下<<和|运算,实际就是将时间戳、机器码和序列号移动到snowflake中相应的位置。
         * @return long
         */
        return ((timestamp - twePoch) << timestampLeftShift)
                | (dataCenterId << datacenterIdShift) | (workerId << workerIdShift)
                | sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = Clock.systemUTC().millis();
        // 如果当时时刻的时间戳 <= 上一次生成id的时间戳,就重新生成当前时间;
        // 即确保当前时刻的时间戳,与上一次的时间戳不会重复
        while (timestamp <= lastTimestamp) {
            timestamp = Clock.systemUTC().millis();
        }
        return timestamp;
    }

    // 测试1秒能够生成的id个数
    public static void generateIdsInOneSecond() {
        SnowFlake idWorker = new SnowFlake(1, 1);
        long start = Clock.systemUTC().millis();
        int i = 0;
        for (; Clock.systemUTC().millis() - start < 1000; i++) {
            idWorker.nextId();
        }
        long end = Clock.systemUTC().millis();
        System.out.println("耗时:" + (end - start));
        System.out.println("生成id个数:" + i);
    }

    public static void main(String[] args) {
        generateIdsInOneSecond();
    }
}

测试结果:

耗时:1000
生成id个数:4082481

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

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

相关文章

2022王道OS 1.2 操作系统的发展与分类

2022 王道OS 操作系统的发展与分类 文章目录2022 王道OS 操作系统的发展与分类知识总览OS的发展与分类手工操作阶段批处理阶段--单道批处理系统批处理阶段--多道批处理系统分时操作系统实时操作系统其他几种OS知识回顾与重要考点文章目录2022 王道OS 操作系统的发展与分类知识总…

【Android App】勾勒出三维立方体和球体以及它们的转动图形讲解及实战(附源码和演示视频 超详细)

需要源码或运行有问题请点赞关注收藏后评论区留言~~~ 一、轮廓勾勒 勾勒三维物体轮廓线条的步骤如下&#xff1a; &#xff08;1&#xff09;调用glEnableClientState方法启用顶点开关 &#xff08;2&#xff09;调用glVertexPointer方法指定三维物体的顶点坐标集合 &#…

单体分层应用架构剖析

分层单体架构风格是分层思想在单体架构中的应用&#xff0c;其关注于技术视角的职责分层。同时&#xff0c;基于不同层变化速率的不同&#xff0c;在一定程度上控制变化在系统内的传播&#xff0c;有助于提升系统的稳定性。但这种技术视角而非业务视角的关注点隔离&#xff0c;…

Unity-2D游戏-打击感与敌人AI

前言 最近快搞毕设了&#xff0c;学一些Unity2D游戏开发的知识&#xff0c;发现b站宝藏up主奥飒姆Awesome的两个蛮不错的教程&#xff0c;我想简单记录一下它这个游戏设计的方法。 我不一点点实现了&#xff0c;就是分析一下大致框架&#xff08;方便以后套用&#xff09; 资…

广东电子MES系统在电子厂实施的功能和流程

1、电子行业特点电子行业为典型的离散性加工企业&#xff0c;其管理核心的问题在于SN号的管理和物料追溯&#xff0c;即产品、半成品、关键部件都有SN号&#xff0c;且需要实现物料追溯。2、电子行业MES解决方案针对行业需求&#xff0c;我们提供了如下的解决方案&#xff1a;采…

【Python模块】日期时间

在平时开发工作中&#xff0c;我们经常需要用到日期时间&#xff0c;比如日志记录、日期时间的计算、时间字段的赋值等。Python 提供了 time 模块、datatime 模块及子模块、calendar 模块等内置模块&#xff0c;可实现对日期时间的设置、获取、转换等常见操作。 一、日期时间的…

基于STM32G431嵌入式学习笔记——七、定时器定时

一、题目引入 上述为第13届蓝桥杯省赛节选内容&#xff0c;为了研究定时器的机理并独立书写计时函数&#xff0c;上述内容简化为以下要求&#xff1a; ①按下B4按键&#xff0c;LD1点亮5s后熄灭 ②按下B3按键&#xff0c;LD2以0.1秒为间隔切换亮灭状态 二、基础知识 定时器相…

请问各位程序员,是我的思维方式有错误吗?

你好呀&#xff0c;我是歪歪。 前几天知乎给我推送了一个问题&#xff0c;我点进去一看&#xff0c;好家伙&#xff0c;竟然把我血压都看上来了是怎么回事。 我先把问题复述一遍&#xff0c;聊天记录比较长&#xff0c;但是看的过程中容易冲动&#xff0c;注意控制情绪&#…

麦芽糖-聚乙二醇-人血清白蛋白,HAS-PEG-maltose

中文名称&#xff1a;麦芽糖-人血清白蛋白 英文名称&#xff1a;maltose-HAS 别称&#xff1a;人血清白蛋白修饰麦芽糖&#xff0c;HAS-麦芽糖 PEG接枝修饰麦芽糖 麦芽糖-聚乙二醇-人血清白蛋白 HAS-PEG-maltose 人血清白蛋白-PEG-麦芽糖 纯度&#xff1a;95% 存储条件…

物料搬运装置及控制系统设计(CAD+PLC)

目 录 1 绪论 1 1.1 物料搬运装置的概述 1 1.2 物料搬运装置的发展史 1 1.3 气动技术及气动物料搬运装置的发展过程 2 1.4 物料搬运装置未来的发展趋势 3 1.5 本课题研究内容 6 1.6 课题研究的意义 6 2 物料搬运装置的总体设计方案 7 2.1 物料搬运装置的工作原理及系统组成 7 2…

动态规划 DP专题

跟着ygg的dp题单刷的dp 1.代码源每日一题 Div1 连续子序列 分析&#xff1a; dp数组开成map&#xff0c;则状态转移式dp[i] max(dp[i - 1] 1, dp[i]) AC代码&#xff1a; #include <bits/stdc.h>using namespace std; typedef long long ll; #define int ll #define …

Day814.电商系统表设计优化案例分析 -Java 性能调优实战

电商系统表设计优化案例分析 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于电商系统表设计优化案例分析。 如果在业务架构设计初期&#xff0c;表结构没有设计好&#xff0c;那么后期随着业务以及数据量的增多&#xff0c;系统就很容易出现瓶颈。 如果表结构扩展…

CentOS7.9 安装配置MySQL5.7.30

1.下载mysql安装包&#xff1a;mysql-5.7.30-linux-glibc2.12-x86_64.tar.gz 2. 卸载自带的mariadb和mysql 检查是否安装了mariadb和mysql&#xff0c;有时候默认安装了 rpm -qa | grep mariadb rpm -qa | grep mysql 如果没有&#xff0c;就可以安装mysql&#xff0c;如果有&a…

Centos7搭建SVN代码控制服务器

Centos7搭建SVN代码控制服务器检查SVN是否安装创建SVN版本库配置代码库设置允许访问远程仓库的用户帐号密码设置权限控制设置SVN服务配置启动svn与停止启动SVN关闭SVN访问拉取远程仓库代码检查SVN是否安装 1、centos7系统自带SVN rpm -qa subversion2、如果没有则通过yum安装 …

[论文精读|博士论文]面向文本数据的关系抽取关键技术研究

电子科技大学 2022.3.15博士论文 实体关系的方向性语义缺失&#xff0c;使得关系的判别缺乏对文本蕴含语义特征的利用 提出——基于句法关系的方向敏感型句子级关系抽取算法 利用依存句法树结构信息构建双向依存路径结构&#xff08;新的文本策略解决过度剪裁&#xff09;额外…

构建直接序列扩频系统模型(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…

【分析笔记】全志 i2c-sunxi.c 控制器驱动分析

分析平台&#xff1a;全志 A64 内核版本&#xff1a;Linux 4.9 数据手册&#xff1a;Allwinner_A64_User_Manual_V1.1.pdf (whycan.com) 驱动框架 I2C 设备驱动 作为方案应用来说&#xff0c;我们是最经常要动的地方&#xff0c;这一层主要与具体的芯片功能强关联&#xff0c…

RK3588平台开发系列讲解(USB篇)内核 USB 配置

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、USB PHY CONFIG二、USB Host CONFIG三、USB OTG CONFIG四、USB Gadget CONFIG沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍USB的相关配置。 USB 模块的配置及保存和其它内核模块的配置方法一…

英语考试的作文模板

考试需要&#xff0c;但是老是忘记&#xff0c;所以发出来备忘~~~~~ 这里写目录标题1 高频考点&#xff1a;正面话题 (能力/事情/习惯/行为/品质/意识/习惯&#xff09;1.1 题干关键词与结构1.2 开头段1.3 主体段2 一起写范文【正面话题】主体段&#xff1a;重要性怎么做重要性…

【AI学习笔记】jupyter notebook 默认路径修改(超简介,超详细)

文章目录修改前&#xff1a;修改notebook默认路径&#xff1a;1. 找到 Anaconda 的安装目录2. 修改 notebook 安装位置3. 删除"%USERPROFILE%/"内容修改后&#xff1a;【声明&#xff1a;由于我的电脑有 Anaconda3的root环境 和 名为TensorFlow 的 Anaconda虚拟环境&…