Java 通过手写分布式雪花SnowFlake生成ID方法详解


Posted in Java/Android onApril 07, 2022

SnowFlake算法

SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图:

Java 通过手写分布式雪花SnowFlake生成ID方法详解

分为四段:

第一段: 1位为未使用,永远固定为0。

(因为二进制中最高位是符号位,1表示负数,0表示正数。生成的id一般都是用正整数,所以最高位固定为0 )

第二段: 41位为毫秒级时间(41位的长度可以使用69年)

第三段: 10位为workerId(10位的长度最多支持部署1024个节点)

(这里的10位又分为两部分,第一部分5位表示数据中心ID(0-31)第二部分5位表示机器ID(0-31))

第四段: 12位为毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

代码实现:

import java.util.HashSet;
import java.util.concurrent.atomic.AtomicLong;

public class SnowFlake {

    //时间 41位
    private static long lastTime = System.currentTimeMillis();

    //数据中心ID 5位(默认0-31)
    private long datacenterId = 0;
    private long datacenterIdShift = 5;

    //机房机器ID 5位(默认0-31)
    private long workerId = 0;
    private long workerIdShift = 5;

    //随机数 12位(默认0~4095)
    private AtomicLong random = new AtomicLong();
    private long randomShift = 12;
    //随机数的最大值
    private long maxRandom = (long) Math.pow(2, randomShift);

    public SnowFlake() {
    }

    public SnowFlake(long workerIdShift, long datacenterIdShift){
        if (workerIdShift < 0 ||
                datacenterIdShift < 0 ||
                workerIdShift + datacenterIdShift > 22) {
            throw new IllegalArgumentException("参数不匹配");
        }
        this.workerIdShift = workerIdShift;
        this.datacenterIdShift = datacenterIdShift;
        this.randomShift = 22 - datacenterIdShift - workerIdShift;
        this.maxRandom = (long) Math.pow(2, randomShift);
    }

    //获取雪花的ID
    private long getId() {
        return lastTime << (workerIdShift + datacenterIdShift + randomShift) |
                workerId << (datacenterIdShift + randomShift) |
                datacenterId << randomShift |
                random.get();
    }

    //生成一个新的ID
    public synchronized long nextId() {
        long now = System.currentTimeMillis();

        //如果当前时间和上一次时间不在同一毫秒内,直接返回
        if (now > lastTime) {
            lastTime = now;
            random.set(0);
            return getId();
        }

	//将最后的随机数,进行+1操作
        if (random.incrementAndGet() < maxRandom) {
            return getId();
        }

        //自选等待下一毫秒
        while (now <= lastTime) {
            now = System.currentTimeMillis();
        }

        lastTime = now;
        random.set(0);
        return getId();

    }

    //测试
    public static void main(String[] args) {
        SnowFlake snowFlake = new SnowFlake();
        HashSet<Long> set = new HashSet<>();
        for (int i = 0; i < 10000; i++) {
            set.add(snowFlake.nextId());
        }
        System.out.println(set.size());
    }

}

代码中获取id的方法利用位运算实现

Java 通过手写分布式雪花SnowFlake生成ID方法详解

 1  |                    41                        |  5  |   5  |     12      

   0|0001100 10100010 10111110 10001001 01011100 00|00000|0 0000|0000 00000000 //41位的时间

   0|000000‭0 00000000 00000000 00000000 00000000 00|10001|0 0000|0000 00000000 //5位的数据中心ID

   0|0000000 00000000 00000000 00000000 00000000 00|00000|1 1001|0000 00000000 //5为的机器ID

or 0|0000000 00000000 00000000 00000000 00000000 00|00000|0 0000|‭0000 00000000‬ //12位的sequence

------------------------------------------------------------------------------------------

   0|0001100 10100010 10111110 10001001 01011100 00|10001|1 1001|‭0000 00000000‬ //结果:910499571847892992

SnowFlake优点:

所有生成的id按时间趋势递增整个分布式系统内不会产生重复id(因为有datacenterId和workerId来做区分) SnowFlake不足:

由于SnowFlake强依赖时间戳,所以时间的变动会造成SnowFlake的算法产生错误。

到此这篇关于Java 通过手写分布式雪花SnowFlake生成ID方法详解的文章就介绍到这了,更多相关Java SnowFlake内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Java/Android 相关文章推荐
springBoot基于webSocket实现扫码登录
Jun 22 Java/Android
Netty结合Protobuf进行编解码的方法
Jun 26 Java/Android
新手初学Java网络编程
Jul 07 Java/Android
Java日常练习题,每天进步一点点(38)
Jul 26 Java/Android
Java实现二分搜索树的示例代码
Mar 17 Java/Android
使用Java去实现超市会员管理系统
Mar 18 Java/Android
SpringBoot2零基础到精通之数据与页面响应
Mar 22 Java/Android
Android Flutter实现3D动画效果示例详解
Apr 07 Java/Android
Java存储没有重复元素的数组
Apr 29 Java/Android
多线程Spring通过@Scheduled实现定时任务
May 25 Java/Android
Android中的Launch Mode详情
Jun 05 Java/Android
Mybatis 一级缓存和二级缓存原理区别
Sep 23 Java/Android
Java详细解析==和equals的区别
Apr 07 #Java/Android
Java 超详细讲解hashCode方法
Apr 07 #Java/Android
Java 关于String字符串原理上的问题
Apr 07 #Java/Android
Java虚拟机内存结构及编码实战分享
Java Lambda表达式常用的函数式接口
Apr 07 #Java/Android
Android Rxjava3 使用场景详解
Apr 07 #Java/Android
Java GUI编程菜单组件实例详解
You might like
php页码形式分页函数支持静态化地址及ajax分页
2014/03/28 PHP
一个完整的php文件上传类实例讲解
2015/10/27 PHP
Smarty环境配置与使用入门教程
2016/05/11 PHP
详解关于php的xdebug配置(编辑器vscode)
2019/01/29 PHP
javascript删除数组元素并且数组长度减小的简单实例
2014/02/14 Javascript
HTML,CSS,JavaScript速查表推荐
2014/12/02 Javascript
jquery+ajax请求且带返回值的代码
2015/08/12 Javascript
Bootstrap 粘页脚效果
2016/03/28 Javascript
JS两个数组比较,删除重复值的巧妙方法(推荐)
2016/06/03 Javascript
基于JS代码实现图片在页面中旋转效果
2016/06/16 Javascript
JSONP跨域请求实例详解
2016/07/04 Javascript
Bootstrap轮播图的使用和理解4
2016/12/14 Javascript
基于JavaScript实现图片剪切效果
2017/03/07 Javascript
js实现多行文本框统计剩余字数功能
2017/03/28 Javascript
javascript数据结构中栈的应用之符号平衡问题
2017/04/11 Javascript
vue.js移动端app之上拉加载以及下拉刷新实战
2017/09/11 Javascript
使用3D引擎threeJS实现星空粒子移动效果
2020/09/13 Javascript
记一次Vue.js混入mixin的使用(分权限管理页面)
2019/04/17 Javascript
Vue开发之封装上传文件组件与用法示例
2019/04/25 Javascript
Node.js系列之连接DB的方法(3)
2019/08/30 Javascript
微信小程序背景音乐开发详解
2019/12/12 Javascript
Vue检测屏幕变化来改变不同的charts样式实例
2020/10/26 Javascript
[01:51]DAC趣味视频-如何成为职业选手.mp4
2017/04/02 DOTA
python实现随机梯度下降(SGD)
2020/03/24 Python
Python中存取文件的4种不同操作
2018/07/02 Python
处理python中多线程与多进程中的数据共享问题
2019/07/28 Python
浅析python 定时拆分备份 nginx 日志的方法
2020/04/27 Python
英国蛋糕装饰用品一站式商店:Craft Company
2019/03/18 全球购物
口头翻译求职人自荐信
2013/12/07 职场文书
大学自我鉴定
2013/12/20 职场文书
人力管理专业毕业生求职信
2014/02/27 职场文书
给老师的一封建议书
2014/03/13 职场文书
大学生应聘导游自荐信
2014/06/02 职场文书
邀请书格式范文
2015/02/02 职场文书
经费申请报告
2015/05/15 职场文书
Win11怎么把合并的任务栏分开 Win11任务栏合并分开教程
2022/04/06 数码科技