分布式系统ID生成方案

分布式系统ID生成方案

1.雪花算法

/**
 * 一种分布式id生成算法
 */
public class SnowFlake {
    /**
     * 起始时间戳
     */
    private static final long START_TIMESTAMP = 1575129600000L;
    /**
     * 时间戳所占位数,设置为43位,最大可表示时间间隔约为378年(距离START_TIME所设置时间)
     */
    private static final long TIME_BIT = 43;
    /**
     * 机器编号所占位数,设置为8位,表示最多可容纳256台机器
     */
    private static final long MACHINE_BIT = 8;
    /**
     * ID所占位数,设置为12位,表示每毫秒可最多产生的ID数量为4096个
     */
    private static final long SEQUENCE_BIT = 12;
    /**
     * 机器编号最大值
     */
    private final static long MAX_MACHINE = ~(-1L << MACHINE_BIT);
    /**
     * ID序号最大值
     */
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
    /**
     * 机器编号的偏移量
     */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    /**
     * 时间戳的偏移量
     */
    private final static long TIME_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    /**
     * 当前机器编号
     */
    private long machineId;
    /**
     * 当前序号
     */
    private long sequence = 0L;
    /**
     * 上一次时间戳
     */
    private long lastTimestamp;

    public SnowFlake(long machineId) {
        if (machineId < 0 || machineId > MAX_MACHINE) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.machineId = machineId;
        this.lastTimestamp = getCurrentTimestamp();
    }

    public synchronized long nextId() {
        long currentTimestamp = getCurrentTimestamp();
        if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards, Refusing to generate id");
        }
        if (currentTimestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0L) {
                //已超过最大值,阻塞至下一秒
                currentTimestamp = getNextTimeMillis(currentTimestamp);
            }
        } else {
            sequence = 0L;
        }
        this.lastTimestamp = currentTimestamp;
        return (currentTimestamp - START_TIMESTAMP) << TIME_LEFT | machineId << MACHINE_LEFT | sequence;
    }

    /**
     * 获取当前时间戳(毫秒)
     */
    private long getCurrentTimestamp() {
        return System.currentTimeMillis();
    }

    /**
     * 阻塞直至下一毫秒
     */
    private long getNextTimeMillis(long preMills) {
        long cMills = getCurrentTimestamp();
        while (cMills <= preMills) {
            cMills = getCurrentTimestamp();
        }
        return cMills;
    }

}

该方法生成一个长整形数字ID,同一节点产生的ID单调递增。但是在分布式部署的场景下,需要给每一台机器分配一个唯一的workId。

2.数据库

3.随机数+时间戳转32进制

import java.util.concurrent.ThreadLocalRandom;

public class IdUtil {

    //前七位为随机字符串,后9位为毫秒时间戳
    public static String getId() {
        ThreadLocalRandom random = ThreadLocalRandom.current();
        //保证生成的随机数字转换成32进制字符串时长度控制在3位
        long num1 = random.nextInt(30000) + 2000;
        //保证生成的随机数字转换成32进制字符串时长度控制在4位
        long num2 = random.nextInt(1000000) + 40000;
        //转换成32进制字符串时长度在9位
        long ts = System.currentTimeMillis() * 2;
        return to32ModStr(num1) + to32ModStr(num2) + to32ModStr(ts);
    }

    //32个字符元素,移除了4个容易和数字混淆的字母 g l o q
    private final static char[] digits = {
            '0', '1', '2', '3', '4', '5', '6', '7',
            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
            'h', 'i', 'j', 'k', 'm', 'n', 'p', 'r',
            's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
    };

    //长整型数字val转32进制字符串,自定义字符串,不是标准32进制字符顺序
    private static String to32ModStr(long val) {
        int mag = Long.SIZE - Long.numberOfLeadingZeros(val);
        int cl = Math.max(((mag + 4) / 5), 1);
        char[] buf = new char[cl];
        int radix = 1 << 5;
        int mask = radix - 1;
        do {
            buf[--cl] = digits[((int) val) & mask];
            val >>>= 5;
        } while (val != 0 && cl > 0);
        return new String(buf);
    }

}

该方法生成16位的字符串ID,前7位字符串为随机数字,后9位字符串为时间戳(精确到毫秒)。

前7位字符串被拆分为3位和4位两部分。如果不拆分,7位32进制字符已经超过了int范围,而且为了保证长度为7位,容易导致前几位字符变化范围小。

该方法产生的ID适用于分布式环境下的日志TraceId。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值