JAVA 生成唯一ID

Twitter-Snowflake算法是Twitter为解决分布式系统中生成唯一ID的需求而设计的。它利用63位long型变量,其中41位用于表示时间戳,10位表示进程ID,12位表示序列号,确保了高效率和强唯一性。

这是twitter的一个id生成算法

Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同。

首先我们需要一个long类型的变量来保存这个生成的id,第一位固定为0,因为id都是正数嘛,还剩63位,用x位表示毫秒时间戳,用y位表示进程id,用z位表示同一个时间戳下的序列号,x+y+z=63。我们直接看代码
 


package com.example.debugdemo;

import java.text.SimpleDateFormat;
import java.util.HashSet;
import java.util.Set;

/**
 *
 */
public class IdGenerator {
    /**
     * SnowFlake算法 64位Long类型生成唯一ID 第一位0,表明正数 2-42,41位,表示毫秒时间戳差值,起始值自定义
     * 43-52,10位,机器编号,5位数据中心编号,5位进程编号 53-64,12位,毫秒内计数器 本机内存生成,性能高
     *
     * 主要就是三部分: 时间戳,进程id,序列号 时间戳41,id10位,序列号12位
     *
     * @author bacyang
     * @param args
     */

    private final static long beginTs = 1483200000000L;

    private long lastTs = 0L;

    private long processId;
    private int processIdBits = 10;

    private long sequence = 0L;
    private int sequenceBits = 12;

    // 10位进程ID标识
    public IdGenerator(long processId) {
        if (processId > ((1 << processIdBits) - 1)) {
            throw new RuntimeException("进程ID超出范围,设置位数" + processIdBits + ",最大"
                    + ((1 << processIdBits) - 1));
        }
        this.processId = processId;
    }

    protected long timeGen() {
        return System.currentTimeMillis();
    }

    public synchronized long nextId() {
        long ts = timeGen();
        if (ts < lastTs) {// 刚刚生成的时间戳比上次的时间戳还小,出错
            throw new RuntimeException("时间戳顺序错误");
        }
        if (ts == lastTs) {// 刚刚生成的时间戳跟上次的时间戳一样,则需要生成一个sequence序列号
            // sequence循环自增
            sequence = (sequence + 1) & ((1 << sequenceBits) - 1);
            // 如果sequence=0则需要重新生成时间戳
            if (sequence == 0) {
                // 且必须保证时间戳序列往后
                ts = nextTs(lastTs);
            }
        } else {// 如果ts>lastTs,时间戳序列已经不同了,此时可以不必生成sequence了,直接取0
            sequence = 0L;
        }
        lastTs = ts;// 更新lastTs时间戳
        return ((ts - beginTs) << (processIdBits + sequenceBits)) | (processId << sequenceBits)
                | sequence;
    }

    protected long nextTs(long lastTs) {
        long ts = timeGen();
        while (ts <= lastTs) {
            ts = timeGen();
        }
        return ts;
    }

    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        IdGenerator ig = new IdGenerator(1023);
        String str = "20190315";
        System.out.println(new SimpleDateFormat("YYYYMMDD").parse(str).getTime());
        Set<Long> set = new HashSet<Long>();
        long begin = System.nanoTime();
        for (int i = 0; i < 10; i++) {
            set.add(ig.nextId());
        }
        System.out.println("time=" + (System.nanoTime() - begin)/1000.0 + " us");
        System.out.println(set.size());
        System.out.println(set);
    }
}

41的时间戳,存储当前时间戳与开始时间戳的差值,大概可以用69年,当然x,y,z可以自己根据情况分配,不是固定的。

此方法同样是本地生成,效率非常高,唯一性满足度很高,只需要以上一个类就行了,每个进程启动时,分配不同的processId即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值