分布式高并发生成唯一标识id:snowflake算法

 

目录

snowflake算法简介

位数说明

1.符号位(1位)

2.linux时间戳(41位)

3.机房序号(5位)

4.机器序号(5位)

5.自增序列(12位)

sonwflake算法Java实现

性能与正确性验证

1.单线程测试snowflake算法的性能

2.多线程测试生成的id是否有重复

3.LRU算法验证id是否有重复


snowflake算法简介

在大型分布式系统中,迫切需要唯一id,唯一id的生成方式有很多种,可以使用uuid,可以使用redis的自增序列,可以使用数据库的自增ID,每种方式各有优缺点,今天给大家介绍一下如何使用snowflake算法生成唯一id。

long类型占64位,我们把64位划分为几个模块,第1位为符号位,接下来的41位存放linux时间戳,中间10位存放机房序号和机器序号,最后12位存放自增序列。

0  - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000  - 000000000000

符号位       -         linux时间戳        -          机房序号 - 机器序号   -  自增序列

位数说明

1.符号位(1位)

第一位是符号位,符号位为0,表示正数。

2.linux时间戳(41位)

linux时间戳占最高位的41位,是时间戳的差值,比如我们定义2015年1月1日为参照时间,那么是当前时间减去参照时间之后的值存入这41位中。最多可以支持 (Math.pow(2,41)-1)/(1000l*60*60*24*365)=69年。

3.机房序号(5位)

Math.pow(2,5)-1=31,所以最多可以有31个机房。

4.机器序号(5位)

Math.pow(2,5)-1=31,所以每个机房最多可以有31台机器。

5.自增序列(12位)

Math.pow(2,12)-1=4095,所以每个机房中的每台机器同一毫秒最多生成4095个序列,也就是4095个id。

如果负载均衡做得好,机器也充足的话,一毫秒最多可以生成 31*31*4095=3935295个无重复的id。

sonwflake算法Java实现


/**
 * @ClassName Snowflake
 * @Description 使用snowflake算法生成id
 * @Author boy
 * @Date 2019/6/11 11:30 AM
 */
public class Snowflake {

    //机群所占位数
    private final static int fleetBit = 5;
    //机器码所占位数
    private final static int machineBit = 5;
    //自增序列所占位数
    private final static int sequenceBit = 12;

    //机器码位移量
    private final static int machineDisplacement = sequenceBit;
    //机群位移量
    private final static int fleetDisplacement = machineBit + sequenceBit;
    //时间戳位移量
    private final static int timeDisplacement = fleetBit + machineBit + sequenceBit;

    //参照时间(2018-01-01 00:00:00)
    private final static long referenceTime = 1514736000000l;

    //自增序列最大值
    private final static int sequenceMax = -1^(-1<<12);

    //自增序列
    private static int sequence = 0;

    //上一次生成自增序列的时间
    private static long lastTimeStamp = -1l;

    /*
     * @Author boy
     * @Description 获取全局唯一id
     * @Date 2019/6/11 5:27 PM
     * @Param [fleetCode, machineCode]
     * @return long
     */
    public static synchronized long getNextId(int fleetCode,int machineCode){
        long currentTimeStamp = getCurrentTimeStamp();
        if (lastTimeStamp==currentTimeStamp){
            if (sequence>=sequenceMax){
                currentTimeStamp = getNextTimeStamp();
                lastTimeStamp = currentTimeStamp;
                sequence = 0;
            } else {
                sequence++;
            }

        } else if (lastTimeStamp<currentTimeStamp){
            sequence = 0;
            lastTimeStamp = currentTimeStamp;
        } else if (lastTimeStamp>currentTimeStamp){

        }
        long id = (currentTimeStamp-referenceTime)<<timeDisplacement |
                fleetCode<<fleetDisplacement |
                machineCode<<machineDisplacement |
                sequence;
        return id;

    }

    /*
     * @Author boy
     * @Description 获取当前时间戳,精确到毫秒
     * @Date 2019/6/11 5:26 PM
     * @Param []
     * @return long
     */
    private static long getCurrentTimeStamp(){
        return System.currentTimeMillis();
    }

    /*
     * @Author boy
     * @Description 当前毫秒自增序列已经达到最大值,等待获取下一毫秒
     * @Date 2019/6/11 5:27 PM
     * @Param []
     * @return long
     */
    private static long getNextTimeStamp(){
        long timeStamp = getCurrentTimeStamp();
        while (timeStamp<=lastTimeStamp){
            timeStamp = getCurrentTimeStamp();
        }
        return timeStamp;
    }

}

性能与正确性验证

1.单线程测试snowflake算法的性能

/**
 * @ClassName SnowflakeTest
 * @Description 单线程测试snowflake算法的性能
 * @Author boy
 * @Date 2019/6/11 7:40 PM
 */
public class SnowflakeTest {

    public static void main(String args[]){
        long startTime = System.currentTimeMillis();
        System.out.println("startTime---------------------------------------" + startTime);
        long num = 100000000l;
        for(int i=0;i<num;i++){
            Snowflake.getNextId(1,2);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("endTime---------------------------------------" + endTime);
        long timeQuantum = startTime - endTime;
        System.out.println("(startTime-endTime)---------------------------------------" + timeQuantum);
        System.out.println("(num/(startTime-endTime))---------------------------------------" + num/timeQuantum);
    }
}

执行结果:

startTime---------------------------------------1560241596412
endTime---------------------------------------1560241620874
(startTime-endTime)----------------------------------------24462
(num/(startTime-endTime))----------------------------------------4087

理论上单台机器每毫秒最多可以生成4095个id。

实际执行结果:生成1亿个ID,单台机器使用了24462毫秒,平均每毫秒生成了4087个id。

2.多线程测试生成的id是否有重复

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @ClassName SnawflakeThreadTest
 * @Description 多线程测试snowflake算法
 * @Author boy
 * @Date 2019/5/29 8:56 PM
 */
public class SnowflakeThreadTest {
    public static void main(String[] args){
        long num = 1000;
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for(int i=0;i<num;i++) {
            executorService.execute(new SnowflakeThread());
        }
        executorService.shutdown();
    }
}

class SnowflakeThread implements Runnable{
    public void run() {
        System.out.println(Snowflake.getNextId(1,2));
    }
}

执行结果:

190915047550230528
190915047550230529
190915047550230530
190915047554424832
190915047554424833
190915047554424834
190915047558619136
190915047558619137
190915047558619138
190915047562813440
190915047562813441
190915047562813442

-----------------

3.LRU算法验证id是否有重复

验证思路:生成1亿个id,每次生成的id和最近的1万个id进行比较,看是否有相同的。

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @ClassName SnowflakeDoubleTest
 * @Description 使用LRU算法验证snowflake算法生成的id是有有重复
 *      生成1亿个id,LRU的深度为1万。每次查询最近的1万个id和新生成的是否会用重复
 * @Author boy
 * @Date 2019/6/13 12:32 PM
 */
public class SnowflakeDoubleTest {

    //队列的深度
    private static int deep = 10000;
    //需要生成的id数量
    private static long num = 100000000l;
    //重复id的数量
    private static int repeatNum = 0;
    public static void main(String args[]){
        LRU lru = new LRU(deep);
        for(int i=0;i<num;i++){
            long id = Snowflake.getNextId(1,2);
            if(lru.get(id)!=null){
                System.out.println(id);
                repeatNum++;
            }else {
                lru.put(id,id);
            }
        }
        System.out.println("重复的id数为:" + repeatNum);
    }
}

/**
 * @ClassName LRU
 * @Description 基于LinkedHashMap的LRU算法
 * @Author boy
 * @Date 2019/6/13 10:27 AM
 */
class LRU extends LinkedHashMap {

    //缓存长度
    int length;

    public LRU(int length){
        super(length,0.75f,true);
        this.length = length;
    }

    /*
     * @Author boy
     * @Description LRU算法的关键方法,超过最大长度就移除尾部元素
     * @Date 2019/6/13 12:24 PM
     * @Param [map]
     * @return boolean
     */
    @Override
    public boolean removeEldestEntry(Map.Entry map){
        return size()>length;
    }
}

执行结果:

重复的id数为:0
发布了32 篇原创文章 · 获赞 38 · 访问量 5491

猜你喜欢

转载自blog.csdn.net/u010482601/article/details/91399554