**【雪花算法】**雪花算法(Snowflake Algorithm)是Twitter开源的用于生成唯一ID的算法,它可以在分布式系统中生成唯一的64位长整数ID。这种ID生成方式既保证了趋势递增,又保证了在不同数据中心、不同机器上生成的ID的唯一性。
**符号位:**占用1位。
时间戳:通常占用41位,表示从某个固定时间点(如1970年1月1日)起的毫秒数。这使得生成的ID具有时间排序性。
机器ID:占用10位,表示生成ID的机器节点,以确保不同机器生成的ID不冲突。
序列号:占用12位,允许同一毫秒内生成多个ID,通常用于支持高并发。
public class SnowflakeIdGenerator {
private static final long EPOCH = 1640995200000L; // 自定义的开始时间戳(2022年1月1日)
private static final long MACHINE_ID_BITS = 10L; // 机器ID的位数
private static final long SEQUENCE_BITS = 12L; // 序列号的位数
private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS); // 机器ID最大值 private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); // 序列号最大值 private long machineId; // 当前机器ID private long sequence = 0L; // 当前序列号 private long lastTimestamp = -1L; // 上次生成ID的时间戳 public SnowflakeIdGenerator(long machineId) { if (machineId > MAX_MACHINE_ID || machineId < 0) { throw new IllegalArgumentException("Machine ID can't be greater than " + MAX_MACHINE_ID + " or less than 0"); } this.machineId = machineId; } public synchronized long generateId() { long timestamp = System.currentTimeMillis(); // 如果当前时间小于上次生成ID的时间戳,说明系统时钟回拨,抛出异常 if (timestamp < lastTimestamp) { throw new RuntimeException("Clock is moving backwards. Rejecting requests until " + lastTimestamp); } // 如果在同一毫秒内,增加序列号 if (lastTimestamp == timestamp) { sequence = (sequence + 1) & SEQUENCE_MASK; // 使用位运算确保序列号循环 // 如果序列号溢出,等待下一毫秒 if (sequence == 0) { timestamp = waitForNextMillis(lastTimestamp); } } else { sequence = 0L; // 如果是新的毫秒,重置序列号 } lastTimestamp = timestamp; // 更新上次生成ID的时间戳 // 组合ID return ((timestamp - EPOCH) << (MACHINE_ID_BITS + SEQUENCE_BITS)) | (machineId << SEQUENCE_BITS) | sequence; } private long waitForNextMillis(long lastTimestamp) { long timestamp = System.currentTimeMillis(); while (timestamp <= lastTimestamp) { timestamp = System.currentTimeMillis(); // 等待下一毫秒 } return timestamp; }}
【雪花算法-修正版】
/* * Long: 1,000,000,000,000,000,000-9,223,372,036,854,775,807,共19位 * 雪花算法变种实现: {HHmmssSSS-9位}+{系统号-2位}+{毫秒递增seq-4位}+{随机数-3位} * */ public class TiDBRandomPrimaryKeyGenerator { //机器号 private String machineId; public static AtomicInteger machineIndex = new AtomicInteger(0); //序列号 private long sequence = 0L; //上一个时间戳,用于保证同一毫秒内序列号不重复。 private long lastTimestamp = -1L; //序列号最大值 private static final long SEQUENCE_MASK = 9999; private String pattern = "HHmmssSSS"; public TiDBRandomPrimaryKeyGenerator() { machineId = getInerMachineId(); this.sequence = (long) (Math.random() * SEQUENCE_MASK); } public synchronized long nextId() { Date currentDate = new Date(); String dateTime = DateFormatUtils.format(currentDate, pattern); String seq = getInerSequence(currentDate.getTime()); String random = generateRandomString(3); StringBuilder id = new StringBuilder(32); id.append(dateTime).append(machineId).append(seq).append(random); return Long.parseLong(String.valueOf(id)); } private synchronized String getInerSequence(long timestamp) { if (timestamp < lastTimestamp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds"); } // 如果是同一毫秒内的时间戳 if (lastTimestamp == timestamp) { sequence = (sequence + 1) % SEQUENCE_MASK; } else { sequence = 0; } lastTimestamp = timestamp; return String.format("%04d", sequence); } private String getInerMachineId( ) { String machineId = String.format("%02d", machineIndex.intValue()); this.machineIndex.incrementAndGet(); return machineId; } public static String generateRandomString(int length) { Random random = new Random(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < length; i++) { // 生成0到9之间的随机数,并转换为字符 char randomChar = (char) (random.nextInt(10) + '0'); sb.append(randomChar); } return sb.toString(); } }
Require:基于雪花算法完成一个局部随机,全局离散没有热点切唯一的数值Id生成器。
燕双嘤2024-09-29 12:30
相关推荐
会员源码网11 小时前
使用`mysql_*`废弃函数(PHP7+完全移除,导致代码无法运行)木心月转码ing12 小时前
Hot100-Day10-T438T438找到字符串中所有字母异位词HelloReader12 小时前
Wi-Fi CSI 感知技术用无线信号“看见“室内的人颜酱15 小时前
二叉树分解问题思路解题模式qianpeng89716 小时前
水声匹配场定位原理及实验董董灿是个攻城狮1 天前
AI视觉连载8:传统 CV 之边缘检测AI软著研究员1 天前
程序员必看:软著不是“面子工程”,是代码的“法律保险”FunnySaltyFish1 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?颜酱2 天前
理解二叉树最近公共祖先(LCA):从基础到变种解析地平线开发者2 天前
SparseDrive 模型导出与性能优化实战