雪花算法生成一个id

package com.shopping.test;

import java.text.SimpleDateFormat;
import java.util.Date;

/*****
 * 雪花算法根据时间戳生成有序的 64 bit 的 Long 类型的唯一 ID
 *
 * 各 bit 含义:
 *   1 bit: 符号位,0 是正数 1 是负数, ID 为正数,所以恒取 0
 *   41 bit: 时间差,我们可以选择一个参考点,用它来计算与当前时间的时间差 (毫秒数),41 bit 存储时间差,足够使用 69 年
 *   10 bit: 机器码,能编码 1024 台机器;可以手动指定含义,比如前5 bit 作为机器编号、后 5 bit 作为进程编号
 *   12 bit: 序列号,同一机器同一毫秒内产生不同的序列号,12 bit 可以支持 4096 个序列号
 * 优点:
 *      灵活配置:机器码可以根据需求灵活配置含义
 *      无需持久化:如果序号自增往往需要持久化,本算法不需要持久化
 *      ID 有含义/可逆性:ID 可以反解出来,对 ID 进行统计分析,可以很简单的分析出整个系统的繁忙曲线,还可以定位到每个机器,在某段时间承担了多少工作,分析出负载均衡情况
 * 高性能:生成速度很快
 *
 */
public class SnowflakeIdWorker {

    /** 开始时间截 (2019-08-21) */
    private final long twepoch = 1566316801000L;

    /** 机器id所占的位数 */
    private final long workerIdBits = 5L;

    /** 数据标识id所占的位数 */
    private final long datacenterIdBits = 5L;

    /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /** 支持的最大数据标识id,结果是31 */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /** 序列在id中占的位数 */
    private final long sequenceBits = 12L;

    /** 机器ID向左移12位 */
    private final long workerIdShift = sequenceBits;

    /** 数据标识id向左移17位(12+5) */
    private final long datacenterIdShift = sequenceBits + workerIdBits;

    /** 时间截向左移22位(5+5+12) */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /** 工作机器ID(0~31) */
    private long workerId;

    /** 数据中心ID(0~31) */
    private long datacenterId;

    /** 毫秒内序列(0~4095) */
    private long sequence = 0L;

    /** 上次生成ID的时间截 */
    private long lastTimestamp = -1L;

    /**
     * 构造函数
     * @param workerId 工作ID (0~31)
     * @param datacenterId 数据中心ID (0~31)
     */
    public SnowflakeIdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can‘t be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can‘t be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    /**
     * 获得下一个ID (该方法是线程安全的)
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        //如果是同一时间生成的,则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //时间戳改变,毫秒内序列重置
        else {
            sequence = 0L;
        }

        //上次生成ID的时间截
        lastTimestamp = timestamp;

        //移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift) //
                | (datacenterId << datacenterIdShift) //
                | (workerId << workerIdShift) //
                | sequence;
    }

    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒为单位的当前时间
     * @return 当前时间(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    /** 测试 */
    public static void main(String[] args) {
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
        for (int i = 0; i < 1000; i++) {
            long id = idWorker.nextId();
            //System.out.println(Long.toBinaryString(id));
            System.out.println(id);
        }

        /*String s="2019-08-21 00:00:01";
        long ts =0;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            Date date = simpleDateFormat.parse(s);
             ts = date.getTime();
        }catch (Exception e){
            ts =0;
        }
        System.out.println(ts);*/

    }
}

原文地址:https://www.cnblogs.com/wyf-love-dch/p/11411649.html

时间: 2024-11-09 08:13:18

雪花算法生成一个id的相关文章

C# 根据twitter的snowflake算法生成唯一ID

C# 版算法: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo 8 { 9 10 /// <summary> 11 /// 根据twitter的snowflake算法生成唯一ID 12 /// snowflake算法 64 位 13 /// 0---000

雪花算法生成id

package com.shopping.test; /** * SnowFlake的结构如下(每部分用-分开):<br> * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br> * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br> * 41位时间截(毫秒级),注意,41位

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

雪花算法中机器id保证全局唯一

关于分布式id的生成系统, 美团技术团队之前已经有写过一篇相关的文章, 详见 Leaf——美团点评分布式ID生成系统 通常在生产中会用Twitter开源的雪花算法来生成分布式主键 雪花算法中的核心就是机器id和数据中心id, 通常来说数据中心id可以在配置文件中配置, 通常一个服务集群可以共用一个配置文件, 而机器id如果也放在配置文件中维护的话, 每个应用就需要一个独立的配置, 难免也会出现机器id重复的问题 解决方案: 1. 通过启动参数去指定机器id, 但是这种方式也会有出错的可能性 2.

使用SnowFlake算法生成唯一ID

转自:https://segmentfault.com/a/1190000007769660 考虑过的方法有 直接用时间戳,或者以此衍生的一系列方法 Mysql自带的uuid 以上两种方法都可以查到就不多做解释了 最终选择了Twitter的SnowFlake算法 这个算法的好处很简单可以在每秒产生约400W个不同的16位数字ID(10进制) 原理很简单 ID由64bit组成 其中 第一个bit空缺 41bit用于存放毫秒级时间戳 10bit用于存放机器id 12bit用于存放自增ID 除了最高位

生成唯一id写法,雪花算法

这个工具直接调用就可以了,用法和写法如下: 代码: 这个是雪花算法的写法: 1 public class SnowFlakeUtil { 2 3 /** 4 * 起始的时间戳 5 */ 6 private final static long START_STMP = 1480166465631L; 7 8 /** 9 * 每一部分占用的位数 10 */ 11 private final static long SEQUENCE_BIT = 12; //序列号占用的位数 12 private fi

分布式唯一ID生成算法-雪花算法

在我们的工作中,数据库某些表的字段会用到唯一的,趋势递增的订单编号,我们将介绍两种方法,一种是传统的采用随机数生成的方式,另外一种是采用当前比较流行的“分布式唯一ID生成算法-雪花算法”来实现. 一.时间戳随机数生成唯一ID 我们写一个for循环,用RandomUtil.generateOrderCode()生成1000个唯一ID,执行结果我们会发现出现重复的ID. /** * 随机数生成util **/ public class RandomUtil { private static fina

mybatis plus 主键生成 Twitter雪花算法 id 及修改id为字符型

mybatis plus配置主键生成策略为2,就是 使用Twitter雪花算法 生成id spring boot中配置为: GlobalConfiguration conf = new GlobalConfiguration(new LogicSqlInjector()); conf.setIdType(5); 这样生成的是long类型的,如果想把这个id 转为字符串类型,则配置主键生成策略为5就行了 https://gitee.com/baomidou/mybatis-plus/blob/de

雪花算法(03)生成时间

前面的理论基础和位运算都了解了,下面我们来生成雪花算法的第一部分,也就是时间部分.时间部分的逻辑起始很简单,就是规定一个起始时间戳,然后用当前时间戳减去起始时间戳,这两个数的差就是我们要的结果.简单看一下代码逻辑: 我们规定的起始时间是 2015-01-01 00:00:00 ,然后获取当前时间,用当前时间减去起始时间,就是我们要的时间部分的数值.这个时候就有一个问题了,如果我们系统的时钟错了会不会有问题?会!所以要做判断!而且系统的时钟错误也是雪花算法的一个致命问题,所以要一定要保证服务器的系