snowflake分布式ID计算

2021-08-19  本文已影响0人  SparkOnly
**
 * 分布式ID生成算法
 * 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的
   41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
 * 优点:1. 生成id不依赖db,完全内存生成,高性能高可用; 2. id呈趋势递增,后续插入索引树的时候性能好
 * 缺点:如果某台机器的系统时钟回拨,有可能造成id冲突,或者id乱序
 */
public class SnowFlake {

    //工作节点ID(0~31)
    private long workerId;
    //数据中心ID(0~31)
    private long datacenterId;
    //毫秒内序列(0~4095)
    private long sequence = 0L;

    //初始时间戳  2010-11-04T09:42:54.657到1970-01-01T00:00:00.000时刻所经过的毫秒数。
    //如果不减去这个值,就浪费了40年的时间戳
    private long twepoch = 1288834974657L;
    //机器id所占位数
    private long workerIdBits = 5L;
    //数据中心id所占位数
    private long datacenterIdBits = 5L;
    //最大支持机器id     2^5-1=31
    private long maxWorkerId = -1L^(-1L << workerIdBits);
    //最大支持数据中心id  2^5-1=31
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    //序列号所占位数
    private long sequenceBits = 12L;

    //机器偏移量(向左移12位)
    private long workerIdShift = sequenceBits;
    //数据中心偏移量(向左移12+5=17位)
    private long datacenterIdShift = sequenceBits + workerIdBits;
    //时间戳偏移量(向左移12+5+5=22位),本身占42位
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    //生成序列的掩码 4095  0b111111111111=0xfff=4095
    // 使用mask可以防止溢出
    private long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long lastTimestamp = -1L;


    public SnowFlake(long workerId, long datacenterId, long sequence){
        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("datacenterId Id can't be greater than %d or less than 0", maxWorkerId));
        }
        System.out.printf("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
                timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);
        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = sequence;
    }
    //使用synchronized,而不是CAS,是因为CAS不适合并发量非常高的情况
    public synchronized long nextId(){
        long timestamp = timeGen();
        //如果时间回拨了,可能id会重复
        if(timestamp < lastTimestamp){
            System.err.printf("clock is moving backwards.  Rejecting requests until %d.", 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;
            //sequence==0说明毫秒内序列已经达到最大值,阻塞等到下个序列
            if(sequence == 0){
                timestamp = tilNextMillis(lastTimestamp);
            }
        }else{
            //时间戳改变时,序列重置
            sequence = 0;
        }

        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampLeftShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) |
                sequence;
    }

    private long tilNextMillis(long lastTimestamp){
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp){
            timestamp = timeGen();
        }
        return timestamp;
    }

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


    public static void main(String[] args) {
        SnowFlake test = new SnowFlake(1, 1, 1);
        for(int i=0;i<10;i++){
            long x = test.nextId();
            System.out.println(x + "\t" + Long.toBinaryString(x));
        }
    }
上一篇下一篇

猜你喜欢

热点阅读