公司动态

SnowflakeId [NTP = Network Time Protocol]

📅 2026/8/18 14:49:19
SnowflakeId [NTP = Network Time Protocol]
UUID SnowflakeId 主键生成策略基本都是基于uuid改造规则业务编码codepackage further.zwf; import java.util.HashSet; import java.util.Set; /** * 简陋雪花算法实现【没有时钟回拨检测】 * 用来演示时钟回拨 → ID重复 * * 41位时间戳 10位机器ID 12位序列号 * 时钟回拨场景 * 服务器 NTP 时间同步、手动改系统时间、虚拟机休眠唤醒后系统时间往回倒退比如刚刚是 17:00:05突然变回 17:00:03 * * author ZengWenfeng * date 2025.01.01 * email 117791303QQ.COM * mobile 13805029595 */ public class SimpleSnowflake { // 起始时间2025-01-01 private static final long START_EPOCH 1735689600000L; // 机器ID固定一台机器 private final long workerId; // 序列号 12位最大4095 private static final long SEQUENCE_BITS 12L; private static final long SEQUENCE_MASK (1 SEQUENCE_BITS) - 1; private long sequence 0L; // 上一次生成ID的时间戳 private long lastTimestamp -1L; public SimpleSnowflake(long workerId) { this.workerId workerId; } /** * 获取ID无任何时钟回拨防护 */ public synchronized long nextId(long mockCurrentTime) { long currTimestamp mockCurrentTime; // 重点简陋实现【没有判断 currTimestamp lastTimestamp】 // 正规实现这里要判断时间回拨抛异常 if (currTimestamp lastTimestamp) { sequence (sequence 1) SEQUENCE_MASK; // 序列号耗尽等待下一毫秒 if (sequence 0) { // 这里简化演示省略等待逻辑 } } else { // 进入新毫秒序列号重置为0 sequence 0L; } lastTimestamp currTimestamp; // 拼装ID return ((currTimestamp - START_EPOCH) (10 12)) | (workerId 12) | sequence; } /** * 测试雪花问题 * * param args */ public static void main(String[] args) { SimpleSnowflake snow new SimpleSnowflake(1L); SetLong idSet new HashSetLong(); long t START_EPOCH 1000; // 【阶段1正常时间同一毫秒连续生成3个ID】 for (int i 0; i 3; i) { long id snow.nextId(t); System.out.println(正常生成ID id); idSet.add(id); } // 模拟时钟大幅向前走先离开这个毫秒 snow.nextId(t 1000); // 【关键时钟回拨时间退回到之前用过的 t】 // 因为 lastTimestamp 现在是 t1000 ! t代码会把 sequence 重置为0 System.out.println( 发生时钟回拨 ); for (int i 0; i 3; i) { long id snow.nextId(t); System.out.println(回拨后生成ID id); if (idSet.contains(id)) { System.err.println(✅【复现成功】检测到重复雪花ID id); } } } }雪花存在理论上都通不过的重复想都不用想 UUID 20250101 123030 000000001 看下怎么配合这用