Spring Boot 实现基于雪花算法的分布式唯一 ID 生成器

在分布式系统中,我们经常需要生成 全局唯一 ID,比如用户 ID、订单号、消息 ID 等。常见的方式有:数据库自增主键、UUID、Redis/Zookeeper 分布式 ID 服务、百度 UidGenerator、美团 Leaf 等。

其中,Twitter 的雪花算法(Snowflake) 是一种轻量级、高性能的分布式唯一 ID 解决方案,被广泛使用。本文将带你在 Spring Boot 中实现并集成雪花算法。


一、为什么需要雪花算法?

  1. 数据库自增 ID

    • 简单,但在分库分表和分布式场景下会产生冲突。

  2. UUID

    • 全球唯一,但字符串太长(36 位),不适合做数据库索引。

  3. 雪花算法(Snowflake)

    • 生成 64 位 long 型 ID,趋势递增,性能高,适合分布式环境。


二、雪花算法原理

Snowflake 算法会生成一个 64 bit 的 long 型整数,格式如下:

| 1位符号位(始终为0) | 41位时间戳 | 5位数据中心ID | 5位机器ID | 12位序列号 |
  • 符号位:始终为 0,保证 ID 为正数。

  • 时间戳:当前毫秒时间戳 - 起始时间戳,可用约 69 年。

  • 数据中心 ID:范围 0~31,可支持 32 个数据中心。

  • 机器 ID:范围 0~31,每个数据中心支持 32 台机器。

  • 序列号:范围 0~4095,每毫秒可生成 4096 个唯一 ID。


三、Spring Boot 实现雪花算法

1. 创建工具类 SnowflakeIdGenerator

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;/*** 雪花算法ID生成器* * 雪花算法生成的ID结构:* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000* 1位符号位 + 41位时间戳 + 5位数据中心ID + 5位机器ID + 12位序列号 = 64位* * 特点:* - 生成的ID趋势递增* - 整个分布式系统内不会产生重复ID* - 能够根据时间戳排序* - 每毫秒能够生成4096个ID*/
@Component
public class SnowflakeIdGenerator {/*** 起始时间戳 (2024-01-01 00:00:00)* 可以使用约69年*/private static final long START_TIMESTAMP = 1704067200000L;/*** 数据中心ID位数*/private static final long DATACENTER_ID_BITS = 5L;/*** 机器ID位数*/private static final long MACHINE_ID_BITS = 5L;/*** 序列号位数*/private static final long SEQUENCE_BITS = 12L;/*** 数据中心ID最大值 (2^5 - 1 = 31)*/private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);/*** 机器ID最大值 (2^5 - 1 = 31)*/private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);/*** 序列号最大值 (2^12 - 1 = 4095)*/private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);/*** 机器ID左移位数*/private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;/*** 数据中心ID左移位数*/private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;/*** 时间戳左移位数*/private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS + DATACENTER_ID_BITS;/*** 数据中心ID*/private final long datacenterId;/*** 机器ID*/private final long machineId;/*** 序列号*/private long sequence = 0L;/*** 上次生成ID的时间戳*/private long lastTimestamp = -1L;/*** 构造函数*/public SnowflakeIdGenerator(@Value("${snowflake.datacenter-id:1}") long datacenterId,@Value("${snowflake.machine-id:1}") long machineId) {if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {throw new IllegalArgumentException(String.format("数据中心ID必须在0-%d之间", MAX_DATACENTER_ID));}if (machineId > MAX_MACHINE_ID || machineId < 0) {throw new IllegalArgumentException(String.format("机器ID必须在0-%d之间", MAX_MACHINE_ID));}this.datacenterId = datacenterId;this.machineId = machineId;}/*** 生成下一个ID* * @return 唯一ID*/public synchronized long nextId() {long timestamp = getCurrentTimestamp();// 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过,抛出异常if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("系统时钟回退,拒绝生成ID。时钟回退了%d毫秒", lastTimestamp - timestamp));}// 如果是同一时间生成的,则进行毫秒内序列if (lastTimestamp == timestamp) {sequence = (sequence + 1) & MAX_SEQUENCE;// 毫秒内序列溢出,等待下一毫秒if (sequence == 0) {timestamp = getNextTimestamp(lastTimestamp);}} else {// 时间戳改变,毫秒内序列重置sequence = 0L;}// 上次生成ID的时间戳lastTimestamp = timestamp;// 移位并通过或运算拼到一起组成64位的IDreturn ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)| (datacenterId << DATACENTER_ID_SHIFT)| (machineId << MACHINE_ID_SHIFT)| sequence;}/*** 生成字符串格式的ID* * @return 字符串ID*/public String nextIdStr() {return String.valueOf(nextId());}/*** 解析ID获取生成时间* * @param id 雪花ID* @return 生成时间戳*/public long parseTimestamp(long id) {return (id >> TIMESTAMP_SHIFT) + START_TIMESTAMP;}/*** 解析ID获取数据中心ID* * @param id 雪花ID* @return 数据中心ID*/public long parseDatacenterId(long id) {return (id >> DATACENTER_ID_SHIFT) & MAX_DATACENTER_ID;}/*** 解析ID获取机器ID* * @param id 雪花ID* @return 机器ID*/public long parseMachineId(long id) {return (id >> MACHINE_ID_SHIFT) & MAX_MACHINE_ID;}/*** 解析ID获取序列号* * @param id 雪花ID* @return 序列号*/public long parseSequence(long id) {return id & MAX_SEQUENCE;}/*** 获取当前时间戳* * @return 当前时间戳*/private long getCurrentTimestamp() {return System.currentTimeMillis();}/*** 获取下一毫秒时间戳* * @param lastTimestamp 上次时间戳* @return 下一毫秒时间戳*/private long getNextTimestamp(long lastTimestamp) {long timestamp = getCurrentTimestamp();while (timestamp <= lastTimestamp) {timestamp = getCurrentTimestamp();}return timestamp;}/*** 获取生成器信息* * @return 生成器信息*/public String getGeneratorInfo() {return String.format("SnowflakeIdGenerator[datacenterId=%d, machineId=%d]", datacenterId, machineId);}
}

2. 在 Spring Boot 中配置 Bean


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;/*** ID生成工具类* 提供静态方法方便调用*/
@Component
public class IdUtils {@Autowiredprivate SnowflakeIdGenerator snowflakeIdGenerator;private static SnowflakeIdGenerator staticSnowflakeIdGenerator;// 静态初始化,确保在没有Spring容器时也能工作static {if (staticSnowflakeIdGenerator == null) {staticSnowflakeIdGenerator = new SnowflakeIdGenerator(1, 1);}}@PostConstructpublic void init() {if (snowflakeIdGenerator != null) {staticSnowflakeIdGenerator = snowflakeIdGenerator;}}/*** 生成雪花算法ID* * @return 唯一ID*/public static long generateId() {return staticSnowflakeIdGenerator.nextId();}/*** 生成雪花算法ID字符串* * @return 唯一ID字符串*/public static String generateIdStr() {return staticSnowflakeIdGenerator.nextIdStr();}/*** 解析ID获取生成时间* * @param id 雪花ID* @return 生成时间戳*/public static long parseTimestamp(long id) {return staticSnowflakeIdGenerator.parseTimestamp(id);}/*** 解析ID获取数据中心ID* * @param id 雪花ID* @return 数据中心ID*/public static long parseDatacenterId(long id) {return staticSnowflakeIdGenerator.parseDatacenterId(id);}/*** 解析ID获取机器ID* * @param id 雪花ID* @return 机器ID*/public static long parseMachineId(long id) {return staticSnowflakeIdGenerator.parseMachineId(id);}/*** 解析ID获取序列号* * @param id 雪花ID* @return 序列号*/public static long parseSequence(long id) {return staticSnowflakeIdGenerator.parseSequence(id);}/*** 获取生成器信息* * @return 生成器信息*/public static String getGeneratorInfo() {return staticSnowflakeIdGenerator.getGeneratorInfo();}
}

3. 测试工具类

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;/*** 简单的雪花算法测试工具* 独立运行,不依赖Spring容器*/
public class SimpleSnowflakeTest {private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");public static void main(String[] args) {System.out.println("=== 雪花算法ID生成器简单测试 ===\n");// 创建生成器实例SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);// 显示配置信息showInfo(generator);// 基础功能测试testBasicGeneration(generator);// 批量生成测试testBatchGeneration(generator);// 性能测试testPerformance(generator);// 唯一性测试testUniqueness(generator);// 递增性测试testIncrement(generator);System.out.println("\n=== 测试完成 ===");}/*** 显示配置信息*/private static void showInfo(SnowflakeIdGenerator generator) {System.out.println("📋 配置信息:");System.out.println("  数据中心ID: 1");System.out.println("  机器ID: 1");System.out.println("  当前时间: " + LocalDateTime.now().format(FORMATTER));System.out.println();}/*** 基础ID生成测试*/private static void testBasicGeneration(SnowflakeIdGenerator generator) {System.out.println("🔧 基础ID生成测试:");// 生成单个IDlong id1 = generator.nextId();System.out.println("  单个ID: " + id1);// 连续生成几个IDSystem.out.println("  连续生成5个ID:");for (int i = 0; i < 5; i++) {long id = generator.nextId();System.out.println("    ID " + (i + 1) + ": " + id);}System.out.println();}/*** 批量生成测试*/private static void testBatchGeneration(SnowflakeIdGenerator generator) {System.out.println("📦 批量生成测试:");int count = 10;List<Long> ids = new ArrayList<>();long startTime = System.currentTimeMillis();for (int i = 0; i < count; i++) {ids.add(generator.nextId());}long endTime = System.currentTimeMillis();System.out.println("  生成 " + count + " 个ID耗时: " + (endTime - startTime) + "ms");System.out.println("  生成的ID:");for (int i = 0; i < ids.size(); i++) {System.out.println("    " + (i + 1) + ": " + ids.get(i));}System.out.println();}/*** 性能测试*/private static void testPerformance(SnowflakeIdGenerator generator) {System.out.println("⚡ 性能测试:");int testCount = 100000;System.out.println("  单线程性能测试 (" + testCount + " 个ID):");long startTime = System.currentTimeMillis();for (int i = 0; i < testCount; i++) {generator.nextId();}long endTime = System.currentTimeMillis();long duration = endTime - startTime;double avgTimePerId = (double) duration / testCount;double idsPerSecond = testCount * 1000.0 / duration;System.out.println("    总耗时: " + duration + "ms");System.out.println("    平均每个ID: " + String.format("%.4f", avgTimePerId) + "ms");System.out.println("    每秒生成: " + String.format("%.0f", idsPerSecond) + " 个ID");System.out.println();}/*** 唯一性测试*/private static void testUniqueness(SnowflakeIdGenerator generator) {System.out.println("🔑 唯一性测试:");int testCount = 100000;Set<Long> ids = ConcurrentHashMap.newKeySet();long startTime = System.currentTimeMillis();for (int i = 0; i < testCount; i++) {Long id = generator.nextId();ids.add(id);}long endTime = System.currentTimeMillis();System.out.println("  生成ID数量: " + testCount);System.out.println("  唯一ID数量: " + ids.size());System.out.println("  唯一性测试: " + (ids.size() == testCount ? "✅ 通过" : "❌ 失败"));System.out.println("  测试耗时: " + (endTime - startTime) + "ms");System.out.println();}/*** 递增性测试*/private static void testIncrement(SnowflakeIdGenerator generator) {System.out.println("📈 递增性测试:");int testCount = 100;List<Long> ids = new ArrayList<>();// 生成测试IDfor (int i = 0; i < testCount; i++) {ids.add(generator.nextId());}// 检查递增性boolean isIncreasing = true;int nonIncreasingCount = 0;for (int i = 1; i < ids.size(); i++) {if (ids.get(i) <= ids.get(i - 1)) {isIncreasing = false;nonIncreasingCount++;}}System.out.println("  测试ID数量: " + testCount);System.out.println("  递增性测试: " + (isIncreasing ? "✅ 通过" : "❌ 失败"));if (!isIncreasing) {System.out.println("  非递增数量: " + nonIncreasingCount);}// 显示前几个和后几个IDSystem.out.println("  前5个ID:");for (int i = 0; i < Math.min(5, ids.size()); i++) {System.out.println("    " + (i + 1) + ": " + ids.get(i));}if (ids.size() > 5) {System.out.println("  后5个ID:");for (int i = ids.size() - 5; i < ids.size(); i++) {System.out.println("    " + (i + 1) + ": " + ids.get(i));}}System.out.println();}/*** 演示ID解析功能*/private static void testIdParsing() {System.out.println("🔍 ID解析测试:");// 创建生成器SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);Long testId = generator.nextId();System.out.println("  测试ID: " + testId);// 解析ID的各个部分long timestamp = IdUtils.parseTimestamp(testId);long datacenterId = IdUtils.parseDatacenterId(testId);long machineId = IdUtils.parseMachineId(testId);long sequence = IdUtils.parseSequence(testId);LocalDateTime generatedTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(timestamp), java.time.ZoneId.systemDefault());System.out.println("  解析结果:");System.out.println("    时间戳: " + timestamp);System.out.println("    生成时间: " + generatedTime.format(FORMATTER));System.out.println("    数据中心ID: " + datacenterId);System.out.println("    机器ID: " + machineId);System.out.println("    序列号: " + sequence);// 计算ID生成到现在的时间差long age = System.currentTimeMillis() - timestamp;System.out.println("    ID年龄: " + age + "ms");System.out.println();}/*** 演示不同配置的生成器*/private static void testDifferentConfigurations() {System.out.println("⚙️  不同配置测试:");// 创建不同配置的生成器SnowflakeIdGenerator generator1 = new SnowflakeIdGenerator(1, 1);SnowflakeIdGenerator generator2 = new SnowflakeIdGenerator(1, 2);SnowflakeIdGenerator generator3 = new SnowflakeIdGenerator(2, 1);System.out.println("  数据中心1-机器1: " + generator1.nextId());System.out.println("  数据中心1-机器2: " + generator2.nextId());System.out.println("  数据中心2-机器1: " + generator3.nextId());System.out.println();}
}

直接运行

4. 在业务中使用


import com.example.common.util.IdUtils;
import com.example.common.util.SnowflakeIdGenerator;
import com.example.common.web.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** ID生成控制器* 提供雪花算法ID的生成和解析服务*/
@RestController
@RequestMapping("/common/id")
@Api(tags = "ID生成服务")
public class IdController {@Autowiredprivate SnowflakeIdGenerator snowflakeIdGenerator;/*** 生成单个雪花算法ID*/@GetMapping("/generate")@ApiOperation("生成单个雪花算法ID")public ApiResponse<Map<String, Object>> generateId() {long id = IdUtils.generateId();Map<String, Object> result = new HashMap<>();result.put("id", id);result.put("idStr", String.valueOf(id));result.put("timestamp", System.currentTimeMillis());result.put("generatedAt", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));return ApiResponse.success(result);}/*** 生成字符串格式的雪花算法ID*/@GetMapping("/generate/string")@ApiOperation("生成字符串格式的雪花算法ID")public ApiResponse<Map<String, Object>> generateIdString() {String idStr = IdUtils.generateIdStr();Map<String, Object> result = new HashMap<>();result.put("idStr", idStr);result.put("id", Long.parseLong(idStr));result.put("timestamp", System.currentTimeMillis());result.put("generatedAt", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));return ApiResponse.success(result);}/*** 批量生成雪花算法ID*/@GetMapping("/generate/batch")@ApiOperation("批量生成雪花算法ID")public ApiResponse<Map<String, Object>> generateBatchIds(@ApiParam("生成数量,最大100") @RequestParam(defaultValue = "10") int count) {if (count <= 0 || count > 100) {return ApiResponse.error(400, "生成数量必须在1-100之间");}List<Long> ids = new ArrayList<>();List<String> idStrs = new ArrayList<>();for (int i = 0; i < count; i++) {long id = IdUtils.generateId();ids.add(id);idStrs.add(String.valueOf(id));}Map<String, Object> result = new HashMap<>();result.put("count", count);result.put("ids", ids);result.put("idStrs", idStrs);result.put("timestamp", System.currentTimeMillis());result.put("generatedAt", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));return ApiResponse.success(result);}/*** 解析雪花算法ID*/@GetMapping("/parse/{id}")@ApiOperation("解析雪花算法ID")public ApiResponse<Map<String, Object>> parseId(@ApiParam("要解析的雪花算法ID") @PathVariable Long id) {try {long timestamp = IdUtils.parseTimestamp(id);long datacenterId = IdUtils.parseDatacenterId(id);long machineId = IdUtils.parseMachineId(id);long sequence = IdUtils.parseSequence(id);LocalDateTime generatedTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());Map<String, Object> result = new HashMap<>();result.put("originalId", id);result.put("originalIdStr", String.valueOf(id));result.put("timestamp", timestamp);result.put("generatedTime", generatedTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));result.put("datacenterId", datacenterId);result.put("machineId", machineId);result.put("sequence", sequence);// 计算ID生成到现在的时间差long timeDiff = System.currentTimeMillis() - timestamp;result.put("ageMs", timeDiff);result.put("ageSeconds", timeDiff / 1000);result.put("ageMinutes", timeDiff / (1000 * 60));return ApiResponse.success(result);} catch (Exception e) {return ApiResponse.error(400, "无效的雪花算法ID: " + e.getMessage());}}/*** 批量解析雪花算法ID*/@PostMapping("/parse/batch")@ApiOperation("批量解析雪花算法ID")public ApiResponse<Map<String, Object>> parseBatchIds(@ApiParam("要解析的ID列表") @RequestBody List<Long> ids) {if (ids == null || ids.isEmpty()) {return ApiResponse.error(400, "ID列表不能为空");}if (ids.size() > 50) {return ApiResponse.error(400, "一次最多解析50个ID");}List<Map<String, Object>> results = new ArrayList<>();List<String> errors = new ArrayList<>();for (Long id : ids) {try {long timestamp = IdUtils.parseTimestamp(id);long datacenterId = IdUtils.parseDatacenterId(id);long machineId = IdUtils.parseMachineId(id);long sequence = IdUtils.parseSequence(id);LocalDateTime generatedTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());Map<String, Object> result = new HashMap<>();result.put("originalId", id);result.put("timestamp", timestamp);result.put("generatedTime", generatedTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));result.put("datacenterId", datacenterId);result.put("machineId", machineId);result.put("sequence", sequence);results.add(result);} catch (Exception e) {errors.add("ID " + id + " 解析失败: " + e.getMessage());}}Map<String, Object> response = new HashMap<>();response.put("totalCount", ids.size());response.put("successCount", results.size());response.put("errorCount", errors.size());response.put("results", results);if (!errors.isEmpty()) {response.put("errors", errors);}return ApiResponse.success(response);}/*** 获取ID生成器信息*/@GetMapping("/info")@ApiOperation("获取ID生成器信息")public ApiResponse<Map<String, Object>> getGeneratorInfo() {String generatorInfo = IdUtils.getGeneratorInfo();// 生成一个示例ID用于展示long sampleId = IdUtils.generateId();Map<String, Object> result = new HashMap<>();result.put("generatorInfo", generatorInfo);result.put("sampleId", sampleId);result.put("sampleIdStr", String.valueOf(sampleId));result.put("currentTimestamp", System.currentTimeMillis());result.put("currentTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));// 添加雪花算法的基本信息Map<String, Object> algorithmInfo = new HashMap<>();algorithmInfo.put("name", "Snowflake Algorithm");algorithmInfo.put("totalBits", 64);algorithmInfo.put("timestampBits", 41);algorithmInfo.put("datacenterIdBits", 5);algorithmInfo.put("machineIdBits", 5);algorithmInfo.put("sequenceBits", 12);algorithmInfo.put("maxDatacenterId", 31);algorithmInfo.put("maxMachineId", 31);algorithmInfo.put("maxSequence", 4095);algorithmInfo.put("maxIdsPerMs", 4096);algorithmInfo.put("maxIdsPerSecond", 4096000);result.put("algorithmInfo", algorithmInfo);return ApiResponse.success(result);}/*** 健康检查 - 测试ID生成性能*/@GetMapping("/health")@ApiOperation("ID生成器健康检查")public ApiResponse<Map<String, Object>> healthCheck() {try {long startTime = System.currentTimeMillis();// 生成100个ID测试性能List<Long> testIds = new ArrayList<>();for (int i = 0; i < 100; i++) {testIds.add(IdUtils.generateId());}long endTime = System.currentTimeMillis();long duration = endTime - startTime;// 验证ID的唯一性long distinctCount = testIds.stream().distinct().count();boolean isUnique = distinctCount == testIds.size();// 验证ID的递增性boolean isIncreasing = true;for (int i = 1; i < testIds.size(); i++) {if (testIds.get(i) <= testIds.get(i - 1)) {isIncreasing = false;break;}}Map<String, Object> result = new HashMap<>();result.put("status", "healthy");result.put("testCount", 100);result.put("durationMs", duration);result.put("avgTimePerIdMs", duration / 100.0);result.put("idsPerSecond", Math.round(100000.0 / duration));result.put("uniqueIds", isUnique);result.put("increasingOrder", isIncreasing);result.put("firstId", testIds.get(0));result.put("lastId", testIds.get(testIds.size() - 1));result.put("checkTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));return ApiResponse.success(result);} catch (Exception e) {Map<String, Object> result = new HashMap<>();result.put("status", "unhealthy");result.put("error", e.getMessage());result.put("checkTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));return ApiResponse.error(500, "ID生成器健康检查失败", result);}}
}

启动项目后,访问:

http://localhost:8080/common/id/generate

{"code": 200,"message": "操作成功","data": {"idStr": "225614442879127552","generatedAt": "2025-09-14 21:51:14","id": 225614442879127550,"timestamp": 1757857874896},"timestamp": 1757857874896
}

就能得到一个 分布式唯一 ID


四、雪花算法的优缺点

优点

  • 本地生成 ID,高性能、低延迟。

  • 64 位 long 型,适合数据库主键,索引效率高。

  • 趋势递增,分库分表更友好。

⚠️ 缺点

  • 时间回拨问题:如果系统时间被调回,可能导致 ID 重复。

  • workerId 配置:需要保证每台机器的 workerId 唯一,否则会冲突。

  • 单机生成上限:每毫秒 4096 个 ID,极端场景可能不够。


五、适用场景

  • 用户 ID、订单号、消息 ID 等需要分布式唯一 ID 的场景。

  • 高并发系统中需要性能高、趋势递增的 ID。

  • 分库分表需要按 ID 范围路由的系统。


六、分布式唯一 ID 生成方案对比

在分布式系统中,唯一 ID 是最基础的需求。常见的实现方式有以下几类:

1. 数据库自增 ID

  • 原理:依赖数据库主键自增特性(AUTO_INCREMENT / Sequence)。

  • 优点

    • 实现简单,无需额外服务。

    • ID 有序,方便分页与索引。

  • 缺点

    • 单库有性能瓶颈,QPS 不高。

    • 扩展到分布式场景需要分库分表,增加复杂性。

  • 适用场景:小型项目、单体应用。


2. UUID / GUID

  • 原理:基于随机数、MAC 地址和时间戳生成 128 位 ID。

  • 优点

    • 本地生成,无需依赖第三方服务。

    • 全球唯一,冲突概率极低。

  • 缺点

    • 长度过长(16 字节 / 36 字符),存储和索引性能差。

    • 无序,不利于数据库分页与索引。

  • 适用场景:日志追踪、分布式追踪、无需排序的业务。


3. Redis INCR

  • 原理:利用 Redis 的原子自增操作生成 ID。

  • 优点

    • 高并发下性能优秀。

    • 简单易用,支持多节点共享。

  • 缺点

    • 依赖 Redis,高可用需额外维护。

    • 扩展性依赖 Redis 集群。

  • 适用场景:电商订单号、消息序列号。


4. Twitter Snowflake

  • 原理:使用 64 位二进制结构(时间戳 + 数据中心 ID + 机器 ID + 序列号)。

  • 优点

    • 高性能,本地生成,不依赖数据库。

    • 数字型,长度适中,递增趋势。

  • 缺点

    • 需要合理分配数据中心和机器 ID。

    • 对系统时钟依赖强,时钟回拨会导致重复 ID。

  • 适用场景:高并发系统(订单 ID、日志 ID、分布式唯一标识)。


5. Zookeeper 分布式 ID

  • 原理:利用 Zookeeper 顺序节点特性生成 ID。

  • 优点

    • 强一致性,保证全局唯一递增。

  • 缺点

    • 性能一般,QPS 不高。

    • 依赖 Zookeeper 集群,运维成本高。

  • 适用场景:金融业务、分布式锁、强一致性场景。


6. 百度 UidGenerator

  • 原理:基于 Snowflake 改造,支持时间回拨处理,依赖数据库分配 WorkerID。

  • 优点

    • 高性能,单机 QPS 可达 600 万。

    • 支持秒级、分级、时级时间单位,灵活。

  • 缺点

    • 依赖数据库分配 WorkerID。

    • 社区活跃度一般,维护较少。

  • 适用场景:高并发业务(订单、交易流水、日志 ID)。


7. 美团 Leaf

  • 原理:提供两种模式:

    1. Segment 模式:基于数据库号段。

    2. Snowflake 模式:本地生成。

  • 优点

    • 双模式保证高可用(DB + Snowflake)。

    • 经过美团生产验证,稳定可靠。

    • Leaf-Snowflake 支持 ZooKeeper 进行 WorkerID 分配,避免冲突。

  • 缺点

    • 系统复杂度较高。

    • 部署依赖 ZooKeeper 或数据库。

  • 适用场景:大规模分布式系统(订单 ID、交易号、日志流水号)。


总结对比表

方案长度顺序性依赖组件性能 (QPS)复杂度适用场景
数据库自增 ID有序数据库小型项目
UUID / GUID无序日志追踪
Redis INCR有序Redis电商订单
Snowflake趋势有序高并发系统
Zookeeper 顺序 ID有序ZK金融业务
百度 UidGenerator有序DB极高高并发场景
美团 Leaf有序DB/ZK极高分布式系统

👉 总结:

  • 小型项目:用数据库自增即可。

  • 日志、追踪:UUID 最方便。

  • 高并发但轻依赖:Twitter Snowflake / 百度 UidGenerator。

  • 大规模分布式系统:美团 Leaf 最优(生产验证,双模式保障)。


七、总结

本文介绍了 雪花算法的原理,并结合 Spring Boot 实现了一个 分布式唯一 ID 生成器
相比 UUID,雪花算法生成的 ID 更短、更高效,适合作为数据库主键。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/98197.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/98197.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/98197.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言初尝试——洛谷

一、C数组&#xff1a;C 语言支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往被认为是一系列相同类型的变量。声明数组在 C 中要声明一个数组&#xff0c;需要指定元素的类型和元素的数量&#xf…

C++八大排序

C排序算法一、概览二、代码实现1.冒泡排序2.插入排序3.希尔排序4.堆排序5.选择排序6.快速排序7.归并排序三、排序时间、空间复杂度总结排序&#xff0c;是C各大算法当中非常常见的一个步骤&#xff08;过程&#xff09;&#xff0c;通常我们使用便捷的algorithmalgorithmalgori…

每天五分钟深度学习:深层神经网络的优势

本文重点 在人工智能领域,深层神经网络(DNN)的崛起标志着技术范式的根本性转变。相较于传统浅层神经网络(如单层感知机、线性回归模型),深层网络通过引入多层隐藏层,实现了对复杂数据模式的深度解析与高效建模。 深层神经网络 神经网络中输入层表示神经网络的第0层,…

相机几何 空间点到像素平面转换

一个空间中点到像素平面转换&#xff0c;需要经过1. 空间坐标系转换到相机坐标系2. 相机坐标系下3D点到相机平面转换3. 相机平面到像素平面转换相机三维空间到像素平面转换1. 3D点到相机平面转换2. 相机平面到像素平面转换涉及到单位的转换&#xff0c;和像素原点到相机平面原点…

webpack5 vue3同一仓库,不同命令切换项目

技术方案&#xff1a;手动输入不同的命令&#xff0c;启动不同项目。实现这种能力本篇文章是通过不同路由划分&#xff0c;进而实现不同项目的划分。所以简单来说就是通过输入不同命令行在webpack中找到不同项目的路由&#xff0c;进而打不同项目的包&#xff0c;实现项目隔离。…

PowerBI实战-制作带有同比及趋势线的双柱状图

一、引言 今天的PowerBI报表的制作相对有一点复杂&#xff0c;我们直接根据最终展示图来讲解&#xff1a; 可以看到&#xff0c;我们今天要制作的图像需要包括以下几点&#xff1a;时间维度的趋势、两种不同维度的数据对比、不同数据标签的展示、不同年份间环比的标签展示以及…

物联网智能网关配置教程:实现注塑机数据经基恩士PLC上传至云平台

一、项目背景随着制造业向智能化、信息化方向快速发展&#xff0c;注塑车间作为塑料制品制造的核心环节&#xff0c;面临着设备协议多样、数据孤岛严重、系统集成困难等问题。某大型注塑企业计划对其老旧车间进行数字化改造&#xff0c;实现设备数据采集、远程监控与MES系统对接…

【实战】预警算法--噪声添加机制

1. 背景 在多变量自联想预测或异常检测场景中&#xff0c;我们常使用带噪自编码器&#xff08;Denoising AutoEncoder&#xff0c;DAE&#xff09;来训练模型&#xff0c;使模型能够从带噪输入中重构原始数据。噪声的添加方式对训练效果、稳定性以及模型用途有显著影响。 2. 两…

ChromaDB探索

关于 ChromaDB、向量与 RAG 系统的核心知识问答总结 ​​Q1: ChromaDB 是什么&#xff1f;它在数据库领域中扮演什么角色&#xff1f;​​​​A:​​ ChromaDB 是一款开源的​​向量数据库​​。它的核心角色是专门为 AI 应用&#xff08;如语义搜索、推荐系统、RAG&#xff09…

C# 基于halcon的视觉工作流-章33-矩状测量

C# 基于halcon的视觉工作流-章33-矩状测量 本章目标&#xff1a; 一、gen_measure_rectangle2准备提取垂直于矩形的直边&#xff1b; 二、measure_pos 提取垂直于矩形或环形弧的直线边缘&#xff1b; 三、measure_pairs提取垂直于矩形或环形弧长轴的直边对&#xff1b; 四、匹配…

Day05_苍穹外卖——Redis店铺营业状态设置

目录1.1 Redis简介1.2 Redis下载与安装1.2.1 Redis下载1.2.2 Redis安装1.3 Redis服务启动与停止1.3.1 服务启动命令1.3.2 客户端连接命令1.3.3 修改Redis配置文件1.3.4 Redis客户端图形工具2. Redis数据类型2.1 五种常用数据类型介绍2.2 各种数据类型特点3. Redis常用命令3.1 字…

双指针:字符串

题目&#xff1a;字符串 题目概述&#xff1a;找包含所有小写字母的最短字符串。 重点思路&#xff1a; right是 < len-1字符 - ‘26’转换成整形再判断&#xff08;写字符a也可以&#xff0c;更准确&#xff09;。 #include <iostream> #include <algorithm>…

HarmonyOS 应用开发深度实践:精通 Stage 模型与 UIAbility 生命周期

好的&#xff0c;请看这篇关于 HarmonyOS Stage 模型与 UIAbility 深度实践的技术文章。 HarmonyOS 应用开发深度实践&#xff1a;精通 Stage 模型与 UIAbility 生命周期 引言 随着 HarmonyOS 4、5 的广泛部署和 HarmonyOS NEXT (API 12) 的发布&#xff0c;华为的分布式操作系…

DEDECMS 小程序插件简介 2.0全新上线

网上有很多的dedecms的小程序插件&#xff0c;但是有的依赖他们第三方、有的需要一定php或sql基础、有的插件免费但是小程序源码价格昂贵&#xff0c;这也是促使我开发dedecms小程序插件的一大原因。2025年9月4日 dedecms小程序插件2.0版本正式上线&#xff0c;由于使用人数减少…

Flink 1.17.2 集群安装部署

Flink集群的安装 1. 集群规划 Ip host Server Note 192.168.10.101 node01 jobManager、TaskManagerRunner 老大和小弟服务 192.168.10.102 node02 TaskManagerRunner 小弟 192.168.10.103 node03 TaskManagerRunner 小弟 注意&#xff1a;本次使用jdk-1.8.0…

[vue.js] 树形结点多选框选择

vue.js前端代码&#xff1a; <template><div><el-tree:data"treeData"node-key"id"show-checkboxref"tree"check-change"handleCheckChange"/><el-button click"getSelectedNodes">获取选中的节点&…

Web 服务器基本工作流程

这是一个关于 ​​Web 服务器基本工作流程​​ 的全面解释。我们以最经典的 ​​客户端-服务器-后端​​ 三层架构为例&#xff0c;并结合你之前遇到的 Nginx 场景进行说明。​​核心角色​​​​客户端 (Client)​​&#xff1a; 通常是 ​​Web 浏览器​​ (Chrome, Firefox)…

IDEA 连接MySQL数据库

一、 连接数据库1、打开连接2、建立连接3、输入用户名和密码二、操作数据库1、选择数据库2、New| Query Console 查询控制台3、写查询语句4、New| SQL Script| sql Generator 生成这个数据库表的SQL结构New | SQL Script | Generate DDL to Query Console 在查询控制台生成…

江协科技STM32课程笔记(二)—外部中断EXTI

二、外部中断EXTI中断&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&#xff0c;处理完成后又返回原来被暂停的位置继续运行。1、stm32中断简介…

Java常见排序算法实现

以下是Java中几种常见排序算法的实现&#xff0c;包括冒泡排序、选择排序、插入排序、快速排序和归并排序。 各排序算法特点说明&#xff1a;冒泡排序&#xff1a; 原理&#xff1a;重复比较相邻元素&#xff0c;将大的元素逐步"冒泡"到数组末尾特点&#xff1a;稳定…