分布式ID生成器

目录

雪花算法

百度UidGenerator

雪花实现创建ID


分布式ID在构建大规模分布式系统时扮演着至关重要的角色,主要用于确保在分布式环境中数据的唯一性和一致性。


雪花算法


SnowFlake算法是Twitter开源的分布式ID生成算法。核心思想就是:使用一个64 bit的 long 型的数字作为全局唯一ID。算法中还引入了时间戳,基本上保证了自增特性。

其特点是将64位的long型ID分为四个部分,分别为:时间戳、机器ID和序列号:

由于在 Java 中 64bit 的整数是 long 类型,所以在 Java 中 SnowFlake 算法生成的 id 就是 long 来存储的。而对于每一个雪花算法服务,需要先指定 10 位的机器码,这个根据自身业务进行设定即可。例如机房号+机器号,机器号+服务号,或者是其他可区别标识的 10 位比特位的整数值都行。

最终效果是,Snowflake算法给出的唯一 ID生成器是一个支持多机房共1024个服务 实例规模、单个服务实例每秒可生成410万个long类型唯一 ID的分布式系统,且此系统可以正常工作69年。

优缺点:

  • 高并发分布式环境下生成不重复 id,每秒可生成百万个不重复 id。基于时间戳,以及同一时间戳下序列号自增,基本保证 id 有序递增。不依赖第三方库或者中间件。算法简单,在内存中进行,效率高。
  • 依赖服务器时间,服务器时钟回拨时可能会生成重复 id。算法中可通过记录最后一个生成 id 时的时间戳来解决,每次生成 id 之前比较当前服务器时钟是否被回拨,避免生成重复 id。

实现:

java 复制代码
public class SnowFlake {

	/**
	 * 起始的时间戳(可设置当前时间之前的邻近时间)
	 */
	private final static long START_STAMP = 1480166465631L;

	/**
	 * 序列号占用的位数
	 */
	private final static long SEQUENCE_BIT = 12;
	/**
	 * 机器标识占用的位数
	 */
	private final static long MACHINE_BIT = 5;
	/**
	 * 数据中心占用的位数
	 */
	private final static long DATA_CENTER_BIT = 5;

	/**
	 * 每一部分的最大值
	 */
	private final static long MAX_DATA_CENTER_NUM = ~(-1L << DATA_CENTER_BIT);
	private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
	private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);

	/**
	 * 每一部分向左的位移
	 */
	private final static long MACHINE_LEFT = SEQUENCE_BIT;
	private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
	private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;

	/**
	 * 数据中心ID(0~31)
	 */
	private final long dataCenterId;
	/**
	 * 工作机器ID(0~31)
	 */
	private final long machineId;
	/**
	 * 毫秒内序列(0~4095)
	 */
	private long sequence = 0L;
	/**
	 * 上次生成ID的时间截
	 */
	private long lastStamp = -1L;

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

	/**
	 * 产生下一个ID
	 */
	public synchronized long nextId() {
		long currStamp = getNewStamp();
		if (currStamp < lastStamp) {
			throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
		}

		if (currStamp == lastStamp) {
			//相同毫秒内,序列号自增
			sequence = (sequence + 1) & MAX_SEQUENCE;
			//同一毫秒的序列数已经达到最大
			if (sequence == 0L) {
				//阻塞到下一个毫秒,获得新的时间戳
				currStamp = getNextMill();
			}
		} else {
			//不同毫秒内,序列号置为0
			sequence = 0L;
		}

		lastStamp = currStamp;

		// 移位并通过或运算拼到一起组成64位的ID
		return (currStamp - START_STAMP) << TIMESTAMP_LEFT //时间戳部分
				| dataCenterId << DATA_CENTER_LEFT       //数据中心部分
				| machineId << MACHINE_LEFT             //机器标识部分
				| sequence;                             //序列号部分
	}

	private long getNextMill() {
		long mill = getNewStamp();
		while (mill <= lastStamp) {
			mill = getNewStamp();
		}
		return mill;
	}

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

	public static void main(String[] args) {
		SnowFlake snowFlake = new SnowFlake(11, 11);

		long start = System.currentTimeMillis();
		for (int i = 0; i < 10; i++) {
			System.out.println(snowFlake.nextId());
		}

		System.out.println(System.currentTimeMillis() - start);
	}
}

博客地址:分布式系列之ID生成器_分布式id生成器-CSDN博客https://blog.csdn.net/lonelymanontheway/article/details/104532828?ops_request_misc=%257B%2522request%255Fid%2522%253A%25221e5d88ad71cd69252001b52b0da29ef4%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=1e5d88ad71cd69252001b52b0da29ef4&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-104532828-null-null.142^v102^pc_search_result_base8&utm_term=%E5%88%86%E5%B8%83%E5%BC%8FId%E7%94%9F%E6%88%90%E5%99%A8&spm=1018.2226.3001.4187分布式唯一ID生成器 最详解-CSDN博客https://blog.csdn.net/2402_82958989/article/details/148616323?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%88%86%E5%B8%83%E5%BC%8FId%E7%94%9F%E6%88%90%E5%99%A8&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-148616323.142^v102^pc_search_result_base8&spm=1018.2226.3001.4187


百度UidGenerator


UidGenerator 是Java实现的, 基于 Snowflake 算法的唯一ID生成器。

UidGenerator 以组件形式工作在应用项目中, 支持自定义 workerId 位数和初始化策略, 从而适用于docker 等虚拟化环境下实例自动重启、漂移等场景。在实现上,UidGenerator 通过借用未来时间来解决 sequence 天然存在的并发限制;采用 RingBuffer 来缓存已生成的 UID, 并行化 UID 的生产和消费, 同时对 CacheLine 补齐,避免了由 RingBuffer 带来的硬件级「伪共享」问题. 最终单机 QPS 可达600万。

项目地址:分布式系列之ID生成器_分布式id生成器-CSDN博客https://blog.csdn.net/lonelymanontheway/article/details/104532828?ops_request_misc=%257B%2522request%255Fid%2522%253A%25221e5d88ad71cd69252001b52b0da29ef4%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=1e5d88ad71cd69252001b52b0da29ef4&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-104532828-null-null.142^v102^pc_search_result_base8&utm_term=%E5%88%86%E5%B8%83%E5%BC%8FId%E7%94%9F%E6%88%90%E5%99%A8&spm=1018.2226.3001.4187

Snowflake 算法描述:指定机器 & 同一时刻 & 某一并发序列,是唯一的。据此可生成一个 64 bits的唯一ID(long)。默认采用上图字节分配方式:

  • sign(1bit):固定1bit符号标识,即生成的UID为正数。
  • delta seconds (28 bits):当前时间,相对于时间基点"2016-05-20"的增量值,单位:秒,最多可支持约8.7年
  • worker id (22 bits):机器id,最多可支持约420w次机器启动。内置实现为在启动时由数据库分配,默认分配策略为用后即弃,后续可提供复用策略。
  • sequence (13 bits):每秒下的并发序列,13 bits可支持每秒8192个并发。

项目解析地址:【分布式 ID】详解百度 uid-generator(基础篇)_uidgenerator-CSDN博客https://blog.csdn.net/laohuangaa/article/details/148881448


雪花实现创建ID


下面代码方案通过 Spring 自动装配 + Redis Lua ,在应用启动时 全局唯一地分配 workId / dataCenterId ,并将其 注入到 SnowflakeIdGenerator 中作为单例使用

java 复制代码
@Data 
public class WorkDataCenterId {

    /**
     * Snowflake 中的 workerId
     * 用来区分不同机器 / 实例
     */
    private Long workId;

    /**
     * Snowflake 中的 dataCenterId
     * 用来区分不同机房 / 集群
     */
    private Long dataCenterId;
}
java 复制代码
public class IdGeneratorAutoConfig {

    /**
     * 将 WorkAndDataCenterIdHandler 注册为 Spring Bean
     *
     * 作用:
     * - 管理 Redis + Lua 分配逻辑
     * - 整个应用生命周期只需要一个实例
     */
    @Bean
    public WorkAndDataCenterIdHandler workAndDataCenterIdHandler(
            StringRedisTemplate stringRedisTemplate){
        return new WorkAndDataCenterIdHandler(stringRedisTemplate);
    }

    /**
     * 在 Spring 启动阶段就获取 workId / dataCenterId
     *
     * - 只在启动时执行一次
     * - 不是每次生成 ID 都访问 Redis
     */
    @Bean
    public WorkDataCenterId workDataCenterId(
            WorkAndDataCenterIdHandler workAndDataCenterIdHandler){
        return workAndDataCenterIdHandler.getWorkAndDataCenterId();
    }

    /**
     * 创建 SnowflakeIdGenerator
     *
     * 依赖:
     * - WorkDataCenterId
     *
     * 最终效果:
     * - SnowflakeIdGenerator 是一个全局单例
     * - workId / dataCenterId 固定
     */
    @Bean
    public SnowflakeIdGenerator snowflakeIdGenerator(
            WorkDataCenterId workDataCenterId){
        return new SnowflakeIdGenerator(workDataCenterId);
    }
}
java 复制代码
public class IdGeneratorConstant {
    /**
     * 机器标识位数
     */
    public static final long WORKER_ID_BITS = 5L;
    public static final long DATA_CENTER_ID_BITS = 5L;
    public static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
    public static final long MAX_DATA_CENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);
}
java 复制代码
@Slf4j // Lombok 注解:自动生成 private static final Logger log
public class WorkAndDataCenterIdHandler {

    /**
     * Redis 中用于记录 snowflake workerId 的 key
     * 本质是一个全局自增 / 分配标识
     */
    private final String SNOWFLAKE_WORK_ID_KEY = "snowflake_work_id";

    /**
     * Redis 中用于记录 snowflake dataCenterId 的 key
     */
    private final String SNOWFLAKE_DATA_CENTER_ID_key = "snowflake_data_center_id";

    /**
     * Lua 脚本执行时使用的 KEYS 列表
     * 对应 Lua 中的:
     * KEYS[1] -> snowflake_work_id
     * KEYS[2] -> snowflake_data_center_id
     */
    public final List<String> keys =
            Stream.of(SNOWFLAKE_WORK_ID_KEY, SNOWFLAKE_DATA_CENTER_ID_key)
                  .collect(Collectors.toList());

    /**
     * Spring 提供的 Redis 操作模板
     * 用来执行 Lua 脚本
     */
    private StringRedisTemplate stringRedisTemplate;

    /**
     * Redis Lua 脚本封装对象
     * Spring 用它来执行 Lua 并解析返回值
     */
    private DefaultRedisScript<String> redisScript;

    /**
     * 构造方法:由 Spring 注入 StringRedisTemplate
     */
    public WorkAndDataCenterIdHandler(StringRedisTemplate stringRedisTemplate){
        this.stringRedisTemplate = stringRedisTemplate;
        try {
            // 创建 Lua 脚本执行对象
            redisScript = new DefaultRedisScript<>();
            // 加载 classpath 下的 lua/workAndDataCenterId.lua
            redisScript.setScriptSource(
                    new ResourceScriptSource(
                            new ClassPathResource("lua/workAndDataCenterId.lua")
                    )
            );
            // 指定 Lua 返回值类型(这里返回 JSON 字符串)
            redisScript.setResultType(String.class);
        } catch (Exception e) {
            // Lua 脚本初始化失败直接记录错误
            log.error("redisScript init lua error", e);
        }
    }

    /**
     * 从 Redis 中获取(或分配)workId 和 dataCenterId
     *
     * @return WorkDataCenterId(包含 workId 和 dataCenterId)
     */
    public WorkDataCenterId getWorkAndDataCenterId(){
        // 返回对象
        WorkDataCenterId workDataCenterId = new WorkDataCenterId();
        try {
            /**
             * Lua 脚本的 ARGV 参数
             */
            String[] data = new String[2];
            data[0] = String.valueOf(IdGeneratorConstant.MAX_WORKER_ID);
            data[1] = String.valueOf(IdGeneratorConstant.MAX_DATA_CENTER_ID);

            /**
             * 执行 Lua 脚本
             */
            String result = stringRedisTemplate.execute(redisScript, keys, data);

            /**
             * Lua 返回的是 JSON 字符串
             * 例如:
             * {"workId":3,"dataCenterId":1}
             */
            workDataCenterId =
                    JSON.parseObject(result, WorkDataCenterId.class);

        } catch (Exception e) {
            log.error("getWorkAndDataCenterId error", e);
        }
        return workDataCenterId;
    }
}

WorkAndDataCenterIdHandler是执行lua脚本的执行器,执行完脚本后获得了 WorkDataCenterId的实体,包好了 workIddataCenterId

WorkDataCenterId在注入到spring上下文的过程中,就调用了 WorkAndDataCenterIdHandler#getWorkAndDataCenterId方法在redis中加载 workIddataCenterId

下面就来分析下加载获取 workIddataCenterId的详细过程:

① 保证 workId / dataCenterId 在 Redis 中存在(初始化),如果 Redis 里还没有,那么第一次启动的第一个实例会把它们都初始化为 0

② 判断是不是"第一次初始化的实例",如果两个 key 都是本次脚本创建的,说明这是整个集群里的第一个服务实例,直接返回{ "workId": 0, "dataCenterId": 0 }

③ 非第一次启动 → 开始"编号分配",后续实例进来,读取当前 workId 和 dataCenterId,然后按一个双层计数器逻辑分配:(优先增长 workId,workId 用完 → 推进 dataCenterId,两者都用完 → 全部归零)

情况 行为
workId < maxWorkerId workId 自增
workId == maxWorkerId 且 dataCenterId < maxDataCenterId dataCenterId 自增
workId == max && dataCenterId == max 两个都重置为 0

④ 返回一个 JSON 给 Java:

Lua 复制代码
{
  "workId": 3,
  "dataCenterId": 1
}
Lua 复制代码
-- redis中work_id的key
local snowflake_work_id_key = KEYS[1]
-- redis中data_center_id的key
local snowflake_data_center_id_key = KEYS[2]
-- worker_id的最大阈值
local max_worker_id = tonumber(ARGV[1])
-- data_center_id的最大阈值
local max_data_center_id = tonumber(ARGV[2])
-- 返回的work_id
local return_worker_id = 0
-- 返回的data_center_id
local return_data_center_id = 0
-- work_id初始化flag
local snowflake_work_id_flag = false
-- data_center_id初始化flag
local snowflake_data_center_id_flag = false
-- 构建并返回JSON字符串
local json_result = string.format('{"%s": %d, "%s": %d}',
        'workId', return_worker_id,
        'dataCenterId', return_data_center_id)

-- 如果work_id不存在,则将值初始化为0
if (redis.call('exists', snowflake_work_id_key) == 0) then
    redis.call('set',snowflake_work_id_key,0)
    snowflake_work_id_flag = true
end
-- 如果data_center_id不存在,则将值初始化为0
if (redis.call('exists', snowflake_data_center_id_key) == 0) then
    redis.call('set',snowflake_data_center_id_key,0)
    snowflake_data_center_id_flag = true
end
-- 如果work_id和data_center_id都是初始化了,那么执行返回初始化的值
if (snowflake_work_id_flag and snowflake_data_center_id_flag) then
    return json_result
end
-- 获得work_id的值
local snowflake_work_id = tonumber(redis.call('get',snowflake_work_id_key))
-- 获得data_center_id的值
local snowflake_data_center_id = tonumber(redis.call('get',snowflake_data_center_id_key))

-- 如果work_id的值达到了最大阈值
if (snowflake_work_id == max_worker_id) then
    -- 如果data_center_id的值也达到了最大阈值
    if (snowflake_data_center_id == max_data_center_id) then
        -- 将work_id的值初始化为0
        redis.call('set',snowflake_work_id_key,0)
        -- 将data_center_id的值初始化为0
        redis.call('set',snowflake_data_center_id_key,0)
    else
        -- 如果data_center_id的值没有达到最大值,将进行自增,并将自增的结果返回
        return_data_center_id = redis.call('incr',snowflake_data_center_id_key)
    end
else
    -- 如果work_id的值没有达到最大值,将进行自增,并将自增的结果返回
    return_worker_id = redis.call('incr',snowflake_work_id_key)
end
return string.format('{"%s": %d, "%s": %d}',
        'workId', return_worker_id,
        'dataCenterId', return_data_center_id)

这样将得到了加载后的包含datacenterIdworkerIdWorkDataCenterId 对象,当创建SnowflakeIdGenerator 时,将WorkDataCenterId注入进去:

注入 datacenterIdworkerId 代码:

java 复制代码
public SnowflakeIdGenerator(WorkDataCenterId workDataCenterId) {
    if (Objects.nonNull(workDataCenterId.getDataCenterId())) {
        this.workerId = workDataCenterId.getWorkId();
        this.datacenterId = workDataCenterId.getDataCenterId();
    }else {
        this.datacenterId = getDatacenterId(maxDatacenterId);
        workerId = getMaxWorkerId(datacenterId, maxWorkerId);
    }
}
java 复制代码
@Slf4j
public class SnowflakeIdGenerator {
    
    /**
     * 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
     */
    private static final long BASIS_TIME = 1288834974657L;
    /**
     * 机器标识位数
     */
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    /**
     * 毫秒内自增位
     */
    private final long sequenceBits = 12L;
    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    /**
     * 时间戳左移动位
     */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    
    private final long workerId;
    
    /**
     * 数据标识 ID 部分
     */
    private final long datacenterId;
    /**
     * 并发控制
     */
    private long sequence = 0L;
    /**
     * 上次生产 ID 时间戳
     */
    private long lastTimestamp = -1L;
    /**
     * IP 地址
     */
    private InetAddress inetAddress;
    
    public SnowflakeIdGenerator(WorkDataCenterId workDataCenterId) {
        if (Objects.nonNull(workDataCenterId.getDataCenterId())) {
            this.workerId = workDataCenterId.getWorkId();
            this.datacenterId = workDataCenterId.getDataCenterId();
        }else {
            this.datacenterId = getDatacenterId(maxDatacenterId);
            workerId = getMaxWorkerId(datacenterId, maxWorkerId);
        }
    }

    public SnowflakeIdGenerator(InetAddress inetAddress) {
        this.inetAddress = inetAddress;
        this.datacenterId = getDatacenterId(maxDatacenterId);
        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
        initLog();
    }

    private void initLog() {
        if (log.isDebugEnabled()) {
            log.debug("Initialization SnowflakeIdGenerator datacenterId:" + this.datacenterId + " workerId:" + this.workerId);
        }
    }

    /**
     * 有参构造器
     *
     * @param workerId     工作机器 ID
     * @param datacenterId 序列号
     */
    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        Assert.isFalse(workerId > maxWorkerId || workerId < 0,
            String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        Assert.isFalse(datacenterId > maxDatacenterId || datacenterId < 0,
            String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        this.workerId = workerId;
        this.datacenterId = datacenterId;
        initLog();
    }

    /**
     * 获取 maxWorkerId
     */
    protected long getMaxWorkerId(long datacenterId, long maxWorkerId) {
        StringBuilder mpid = new StringBuilder();
        mpid.append(datacenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (StringUtils.isNotBlank(name)) {
            /*
             * GET jvmPid
             */
            mpid.append(name.split("@")[0]);
        }
        /*
         * MAC + PID 的 hashcode 获取16个低位
         */
        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * 数据标识id部分
     */
    protected long getDatacenterId(long maxDatacenterId) {
        long id = 0L;
        try {
            if (null == this.inetAddress) {
                this.inetAddress = InetAddress.getLocalHost();
            }
            NetworkInterface network = NetworkInterface.getByInetAddress(this.inetAddress);
            if (null == network) {
                id = 1L;
            } else {
                byte[] mac = network.getHardwareAddress();
                if (null != mac) {
                    id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
                    id = id % (maxDatacenterId + 1);
                }
            }
        } catch (Exception e) {
            log.warn(" getDatacenterId: " + e.getMessage());
        }
        return id;
    }
    
    public long getBase(){
        int five = 5;
        long timestamp = timeGen();
        //闰秒
        if (timestamp < lastTimestamp) {
            long offset = lastTimestamp - timestamp;
            if (offset <= five) {
                try {
                    wait(offset << 1);
                    timestamp = timeGen();
                    if (timestamp < lastTimestamp) {
                        throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            } else {
                throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
            }
        }
        
        if (lastTimestamp == timestamp) {
            // 相同毫秒内,序列号自增
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 同一毫秒的序列数已经达到最大
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 不同毫秒内,序列号置为 1 - 2 随机数
            sequence = ThreadLocalRandom.current().nextLong(1, 3);
        }
        
        lastTimestamp = timestamp;
        
        return timestamp;
    }

    /**
     * 获取分布式id
     *
     * @return id
     */
    public synchronized long nextId() {
        long timestamp = getBase();

        // 时间戳部分 | 数据中心部分 | 机器标识部分 | 序列号部分
        return ((timestamp - BASIS_TIME) << timestampLeftShift)
            | (datacenterId << datacenterIdShift)
            | (workerId << workerIdShift)
            | sequence;
    }
    
    /**
     * 获取订单编号
     *
     * @return orderNumber
     */
    public synchronized long getOrderNumber(long userId,long tableCount) {
        long timestamp = getBase();
        long sequenceShift = log2N(tableCount);
        // 时间戳部分 | 数据中心部分 | 机器标识部分 | 序列号部分 | 用户id基因
        return ((timestamp - BASIS_TIME) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift)
                | (sequence << sequenceShift)
                | (userId % tableCount);
    }

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

    protected long timeGen() {
        return SystemClock.now();
    }

    /**
     * 反解id的时间戳部分
     */
    public static long parseIdTimestamp(long id) {
        return (id>>22)+ BASIS_TIME;
    }
    
    /**
    * 求log2(N)
    * */
    public long log2N(long count) {
        return (long)(Math.log(count)/ Math.log(2));
    }
    
    public long getMaxWorkerId() {
        return maxWorkerId;
    }
    
    public long getMaxDatacenterId() {
        return maxDatacenterId;
    }
}

总结:

  • 在构建SnowflakeIdGenerator时,如果通过lua执行加载获取workDataCenterId失败,则还采取Mybiats-plus的生成策略
  • nextId方法就是获取分布式id的方法,其内部getBase()是更新时间戳的部分,由 时间戳部分 | 数据中心部分 | 机器标识部分 | 序列号部分 这四个部分组成
  • getOrderNumber方法是生成订单编号,使用了基因替换法,来解决在分库分表情况下,使用订单id和用户id查询订单时的全路由问题,关于基因法的详细介绍,可跳转到相关文档
相关推荐
rustfs6 分钟前
RustFS 配置 Cloudflare Tunnel 实现安全访问的详细教程!
分布式·安全·docker·rust·开源
SJLoveIT24 分钟前
CAP理论,顺便讲下BASE
分布式
TTBIGDATA1 小时前
【Hue】Hue 访问 Hadoop 权限问题出现 403 的解决办法
大数据·hadoop·分布式·ambari·hdp·hue·bigtop
【赫兹威客】浩哥1 小时前
【赫兹威客】伪分布式ZooKeeper测试教程
大数据·分布式·zookeeper
小北方城市网1 小时前
Elasticsearch 分布式检索生产级优化:从索引设计到查询性能
java·大数据·运维·redis·分布式·elasticsearch·搜索引擎
啊吧怪不啊吧3 小时前
极致性能的服务器Redis之String类型及相关指令介绍
网络·数据库·redis·分布式·mybatis
BUTCHER511 小时前
Filebeat输出Kafka配置
分布式·kafka
测试人社区-浩辰16 小时前
AI与区块链结合的测试验证方法
大数据·人工智能·分布式·后端·opencv·自动化·区块链
老友@18 小时前
分布式事务完全演进链:从单体事务到 TCC 、Saga 与最终一致性
分布式·后端·系统架构·事务·数据一致性
【赫兹威客】浩哥21 小时前
【赫兹威客】完全分布式ZooKeeper测试教程
分布式·zookeeper·云原生