Java中SnowFlake 雪花算法生成全局唯一id中的问题,时间不连续全为偶数解决

package com.example.springbootshardingjdbc.util;

import java.io.FileOutputStream;

/**
 * 描述: Twitter的分布式自增ID雪花算法snowflake (Java版)
 *
 * @author
 * @create 2018-03-13 12:37
 **/
public class SnowFlake {

    /**
     * 起始的时间戳
     */
    private final static long START_STMP = 1480166465631L;

    /**
     * 每一部分占用的位数
     */
    private final static long SEQUENCE_BIT = 12; //序列号占用的位数
    private final static long MACHINE_BIT = 5;   //机器标识占用的位数
    private final static long DATACENTER_BIT = 5;//数据中心占用的位数

    /**
     * 每一部分的最大值
     */
    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

    /**
     * 每一部分向左的位移
     */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

    private long datacenterId;  //数据中心
    private long machineId;     //机器标识
    private long sequence = 0L; //序列号
    private long lastStmp = -1L;//上一次时间戳

    public SnowFlake(long datacenterId, long machineId) {
        if (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;
    }

    /**
     * 产生下一个ID
     *
     * @param ifEvenNum 是否偶数 true 时间不连续全是偶数  时间连续 奇数偶数 false 时间不连续 奇偶都有  所以一般建议用false
     * @return
     */
    public synchronized long nextId(boolean ifEvenNum) {
        long currStmp = getNewstmp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }
        /**
         * 时间不连续出来全是偶数
         */
        if(ifEvenNum){
            if (currStmp == lastStmp) {
                //相同毫秒内,序列号自增
                sequence = (sequence + 1) & MAX_SEQUENCE;
                //同一毫秒的序列数已经达到最大
                if (sequence == 0L) {
                    currStmp = getNextMill();
                }
            } else {
                //不同毫秒内,序列号置为0
                sequence = 0L;
            }
        }else {
            //相同毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
        }

        lastStmp = currStmp;

        return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
                | datacenterId << DATACENTER_LEFT       //数据中心部分
                | machineId << MACHINE_LEFT             //机器标识部分
                | sequence;                             //序列号部分
    }

    private long getNextMill() {
        long mill = getNewstmp();
        while (mill <= lastStmp) {
            mill = getNewstmp();
        }
        return mill;
    }

    private long getNewstmp() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args)  throws Exception{
        /**
         * 分布式数据中心id
         * 机器id
         */
        SnowFlake snowFlake = new SnowFlake(5, 6);
        for (int i = 0; i < 10; i++) {
            /**
             * 时间连续 奇数偶数都有
             */
            //System.out.println(snowFlake.nextId(true));
            /**
             * 429407369643581440
             * 429407369643581441
             * 429407369643581442
             * 429407369643581443
             * 429407369643581444
             * 429407369643581445
             * 429407369643581446
             * 429407369643581447
             * 429407369643581448
             * 429407369643581449
             */
            //System.out.println(snowFlake.nextId(true));
            /**
             * 时间不连续 原版 获取的id全是偶数
             */
            //Thread.sleep(1);
            /**
             * 429407954194935808
             * 429407954199130112
             * 429407954207518720
             * 429407954211713024
             * 429407954215907328
             * 429407954224295936
             * 429407954249461760
             * 429407954253656064
             * 429407954257850368
             * 429407954262044672
             */
            long snowFlakeId = snowFlake.nextId(false);
            System.out.println(snowFlakeId);
            /**
             * 时间不连续 改版 获取的id为奇偶数
             */
            Thread.sleep(1);
            /**
             * 429408617683496961
             * 429408617691885570
             * 429408617696079875
             * 429408617700274180
             * 429408617704468485
             * 429408617708662790
             * 429408617712857095
             * 429408617717051400
             * 429408617725440009
             * 429408617729634314
             */

        }
    }
}

原文地址:https://www.cnblogs.com/coderdxj/p/12358500.html

时间: 2024-10-09 22:50:18

Java中SnowFlake 雪花算法生成全局唯一id中的问题,时间不连续全为偶数解决的相关文章

高并发分布式系统中生成全局唯一Id汇总

高并发分布式系统中生成全局唯一Id汇总 (转自:http://www.cnblogs.com/baiwa/p/5318432.html) 数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求:   1 不能有单点故障.   2 以时间为序,或者ID里包含时间.这样一是可以少一个索引,二是冷热数据容易分离.   3 可以控制ShardingId.比如某一个用户的文章要放在同一个分片内,这样查询效率高,修改也容易.   

如何在高并发分布式系统中生成全局唯一Id(转)

http://www.cnblogs.com/heyuquan/p/global-guid-identity-maxId.html 又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文,后续再奉上.最近还写了一个发邮件的组件以及性能测试请看 <NET开发邮件发送功能的全面教程(含邮件组件源码)> ,还弄了个MSSQL参数化语法生成器,会在9月整理出来,有兴趣的园友可以关注下我的博客. 分享原由,最近公司用到,并且在找最合适的方案,希望大家多参与讨论和提出新方案.我和我的小伙伴们也

通过Zookeeper学习在分布式系统中生成全局唯一ID

Session是Zookeeper中的会话实体,代表了一个客户端会话.SessionID用来唯一标识一个会话,因此Zookeeper必须保证sessionID的全局唯一性,在每次客户端向服务端发起"会话创建"请求时,服务端都会为其分配一个sessionID.那么Zookeeper是如何实现的呢? 在SessionTracker初始化的时候,会调用initializeNextSession方法来生成一个初始化的sessionID,之后在Zookeeper的正常运行过程中,会在该sessi

如何在高并发分布式系统中生成全局唯一Id

我了解的方案如下-------------------------- 1.  使用数据库自增Id 优势:编码简单,无需考虑记录唯一标识的问题. 缺陷: 1)         在大表做水平分表时,就不能使用自增Id,因为Insert的记录插入到哪个分表依分表规则判定决定,若是自增Id,各个分表中Id就会重复,在做查询.删除时就会有异常. 2)         在对表进行高并发单记录插入时需要加入事物机制,否则会出现Id重复的问题. 3)         在业务上操作父.子表(即关联表)插入时,需要

SnowFlake 生成全局唯一id

public class SnowFlakeUtil { private long workerId; private long datacenterId; private long sequence = 0L; private long twepoch = 1288834974657L; // Thu, 04 Nov 2010 01:42:54 GMT 标记时间 用来计算偏移量,距离当前时间不同,得到的数据的位数也不同 private long workerIdBits = 5L; // 物理

游戏服务器生成全局唯一ID的几种方法

在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使用全局的唯一id,在游戏服务器中,全局唯一的id可以用于将来合服方便,不会出现键冲突.也可以将来在业务增长的情况下,实现分库分表,比如某一个用户的物品要放在同一个分片内,而这个分片段可能是根据用户id的范围值来确定的,比如用户id大于1000小于100000的用户在一个分片内.目前常用的有以下几种:

python 中md5 和 sha1 加密, md5 + os.urandom 生成全局唯一ID

首先先来介绍一下md5 和 sha1 的概念 MD5 MD5的全称是Message-Digest Algorithm 5(信息-摘要算法).128位长度.目前MD5是一种不可逆算法. 具有很高的安全性.它对应任何字符串都可以加密成一段唯一的固定长度的代码. SHA1 SHA1的全称是Secure Hash Algorithm(安全哈希算法) .SHA1基于MD5,加密后的数据长度更长, 它对长度小于264的输入,产生长度为160bit的散列值.比MD5多32位. 因此,比MD5更加安全,但SHA

Java生成全局唯一ID代码演示

看了GitHub上的两个生成唯一ID的算法程序(一个出自百度,一个出自美团),打算运行着试试看,至于原理什么的文档上讲得很详细了,此处不再一一粘贴了,此处只演示代码 https://github.com/baidu/uid-generator https://github.com/zhuzhong/idleaf 百度UID生成器 1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns=&q

生成全局唯一id

http://littcai.iteye.com/blog/1900018 http://www.oschina.net/question/575914_90957 http://www.dengchuanhua.com/132.html twitter/snowflake https://github.com/twitter/snowflake https://twitter.github.io/twitter-server/