美文网首页
snowflake分布式ID计算

snowflake分布式ID计算

作者: SparkOnly | 来源:发表于2021-08-19 08:39 被阅读0次
    **
     * 分布式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));
            }
        }
    

    相关文章

      网友评论

          本文标题:snowflake分布式ID计算

          本文链接:https://www.haomeiwen.com/subject/qhapqltx.html