分布式环境生成唯一订单id方案

分布式环境生成一个唯一id从来不是一个容易的事,不同的节点都独立的各自生成id,高并发性场景下容易生成相同的订单id。


方案1:数据库自增主键

优点:全局唯一、不会重复

缺点:订单id有序、容易被外界爬虫知道业务的订单量数据


方案2:UUID

UUID(Universally Unique Identifier)的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:550e8400-e29b-41d4-a716-446655440000

优点:性能非常高,本地生成,没有网络消耗

缺点:不易于存储,UUID太长,16字节128位,通常以36长度的字符串表示

           由于是无序,长字符串,存储数据库后索引效率比较差

String uuid = UUID.randomUUID().toString()

方案3:时间戳 + ip或者Mac地址

优点:无序、高并发情况下几乎不可能产生相同id,大部分业务可以满足使用

缺点:不适用超高并发,例如1ms可以生成多个订单id的场景,对系统健壮性要求极高的场景


方案4:snowflake(推荐)

Snowflake,Twitter开源的一种分布式ID生成算法。基于64位数实现,下图为Snowflake算法的ID构成图。

分布式环境生成唯一订单id方案_第1张图片

 

整个64-bit由以下组成部分组成

1.第一位

占用1bit,其值始终是0,没有实际作用。

2.时间戳

占用41bit,精确到毫秒,总共可以容纳约69 年的时间。

3.工作机器id

占用10bit,其中高位5bit是数据中心ID(datacenterId),低位5bit是工作节点ID(workerId),最多可以容纳1024个节点。

4.序列号

占用12bit,这个值在同一毫秒同一节点上从0开始不断累加,最多可以累加到4095。

SnowFlake算法在同一毫秒的ID数量 = 1024 X 4096 = 4194304 ,一毫秒可以生成419万的订单id !!!

 

用Java实现SnowFlake算法 , 其实Snowflake算法不难,设计的也很巧妙

/**
 * Created by Zhon.Thao on 2019/10/24.
 *
 * @author Zhon.Thao
 */
public class SnowFlake {

    /**
     * 起始的时间戳:这个时间戳自己随意获取
     */
    private final static long START_MILLS = 1543903501000L;

    /**
     * 每一部分占用的位数
     */
    private final static long SEQUENCE_BIT = 12; //序列号占用的位数

    /**
     * 机器标识占用的位数
     */
    private final static long MACHINE_BIT = 5;

    /**
     * 数据中心占用的位数
     */
    private final static long DATACENTER_BIT = 5;

    /**
     * 用位运算计算出最大支持的数据中心数量:31
     */
    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);

    /**
     * 用位运算计算出最大支持的机器数量:31
     */
    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);

    /**
     * 用位运算计算出12位能存储的最大正整数:4095
     */
    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

    /**
     * 机器标志较序列号的偏移量
     */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;

    /**
     * 数据中心较机器标志的偏移量
     */
    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;

    /**
     * 时间戳较数据中心的偏移量
     */
    private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

    /**
     * 数据中心id
     */
    private long dataCenterId;

    /**
     * 机器标识id
     */
    private long machineId;

    /**
     * 序列号 1ms内
     */
    private static long sequence = 0L;

    /**
     * 上一次的时间戳
     */
    private static long lastMills = -1L;

    /**
     * 如果业务方有datacenterId、machineId使用以下构造方法
     */
    public SnowFlake(long dataCenterId, long machineId) {
        this.dataCenterId = dataCenterId;
        this.machineId = machineId;
    }

    /**
     * 如果业务方觉得配置datacenterId、machineId比较麻烦,可以各自机器随机从最大值里去一个数
     * * 在极高并发下可能会生成相同id
     */
    public SnowFlake() {
        this.dataCenterId = RandomUtils.nextLong(0, MAX_DATACENTER_NUM);
        this.machineId = RandomUtils.nextLong(0, MAX_MACHINE_NUM);
    }

    /**
     * 生成订单ID
     *
     * @return
     */
    public synchronized long getId() {

        /** 获取当前时间戳 */
        long currMills = System.currentTimeMillis();

        /** 如果当前时间戳小于上次时间戳则抛出异常 */
        if (currMills < lastMills) {
            throw new RuntimeException("getId error,currMills < lastMills");
        }

        /** 相同毫秒内 */
        if (currMills == lastMills) {
            //相同毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大
            if (sequence == 0L) {

                /** 获取下一时间的时间戳并赋值给当前时间戳 */
                currMills = getNextMill();
            }
        } else {
            //不同毫秒内,序列号置为0
            sequence = 0L;
        }

        /** 当前时间戳存档记录,用于下次产生id时对比是否为相同时间戳 */
        lastMills = currMills;


        return  (currMills - START_MILLS) << TIMESTMP_LEFT //时间戳部分
                | dataCenterId << DATACENTER_LEFT      //数据中心部分
                | machineId << MACHINE_LEFT            //机器标识部分
                | sequence;                            //序列号部分
    }

    private static long getNextMill() {
        long mill = System.currentTimeMillis();
        while (mill <= lastMills) {
            mill = System.currentTimeMillis();
        }
        return mill;
    }
}

方案五 :  美团点评分布式唯一id生成系统,目前已开源

         文档 : https://tech.meituan.com/2017/04/21/mt-leaf.html
         Github: https://github.com/Meituan-Dianping/Leaf

你可能感兴趣的:(分布式,订单id)