分布式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查询订单时的全路由问题,关于基因法的详细介绍,可跳转到相关文档
相关推荐
oMcLin9 小时前
如何在 Ubuntu 22.04 服务器上实现分布式数据库 Cassandra 集群,优化数据一致性与写入吞吐量
服务器·分布式·ubuntu
马达加斯加D12 小时前
系统设计 --- 使用消息队列解决分布式事务
分布式
遇见火星13 小时前
RabbitMQ 高可用:HAProxy 负载均衡实战指南
分布式·消息队列·rabbitmq·负载均衡·haproxy
Blossom.11814 小时前
基于多智能体协作的自动化数据分析系统实践:从单点工具到全流程智能
运维·人工智能·分布式·智能手机·自动化·prompt·边缘计算
回家路上绕了弯15 小时前
MDC日志链路追踪实战:让分布式系统问题排查更高效
分布式·后端
qq_124987075315 小时前
基于Hadoop的黑龙江旅游景点推荐系统的设计与实现(源码+论文+部署+安装)
大数据·hadoop·分布式·python·信息可视化
笃行客从不躺平16 小时前
分布式中 BASE 理论
分布式
laocooon52385788616 小时前
大专Hadoop课程考试方案设计
大数据·hadoop·分布式
独自破碎E16 小时前
RabbitMQ的交换机有哪几种类型?
分布式·rabbitmq
DeepFlow 零侵扰全栈可观测16 小时前
民生银行云原生业务的 eBPF 可观测性建设实践
运维·开发语言·分布式·云原生·金融·php