SpringBoot集成雪花算法唯一ID的實(shí)現(xiàn)
在分布式系統(tǒng)中,我們經(jīng)常需要生成 全局唯一 ID,比如用戶 ID、訂單號、消息 ID 等。常見的方式有:數(shù)據(jù)庫自增主鍵、UUID、Redis/Zookeeper 分布式 ID 服務(wù)、百度 UidGenerator、美團(tuán) Leaf 等。
其中,Twitter 的雪花算法(Snowflake) 是一種輕量級、高性能的分布式唯一 ID 解決方案,被廣泛使用。本文將帶你在 Spring Boot 中實(shí)現(xiàn)并集成雪花算法。
一、為什么需要雪花算法?
數(shù)據(jù)庫自增 ID
簡單,但在分庫分表和分布式場景下會(huì)產(chǎn)生沖突。
UUID
全球唯一,但字符串太長(36 位),不適合做數(shù)據(jù)庫索引。
雪花算法(Snowflake)
生成 64 位 long 型 ID,趨勢遞增,性能高,適合分布式環(huán)境。
二、雪花算法原理
Snowflake 算法會(huì)生成一個(gè) 64 bit 的 long 型整數(shù),格式如下:
| 1位符號位(始終為0) | 41位時(shí)間戳 | 5位數(shù)據(jù)中心ID | 5位機(jī)器ID | 12位序列號 |
- 符號位:始終為 0,保證 ID 為正數(shù)。
- 時(shí)間戳:當(dāng)前毫秒時(shí)間戳 - 起始時(shí)間戳,可用約 69 年。
- 數(shù)據(jù)中心 ID:范圍 0~31,可支持 32 個(gè)數(shù)據(jù)中心。
- 機(jī)器 ID:范圍 0~31,每個(gè)數(shù)據(jù)中心支持 32 臺機(jī)器。
- 序列號:范圍 0~4095,每毫秒可生成 4096 個(gè)唯一 ID。
三、Spring Boot 實(shí)現(xiàn)雪花算法
1. 創(chuàng)建工具類SnowflakeIdGenerator
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* 雪花算法ID生成器
*
* 雪花算法生成的ID結(jié)構(gòu):
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
* 1位符號位 + 41位時(shí)間戳 + 5位數(shù)據(jù)中心ID + 5位機(jī)器ID + 12位序列號 = 64位
*
* 特點(diǎn):
* - 生成的ID趨勢遞增
* - 整個(gè)分布式系統(tǒng)內(nèi)不會(huì)產(chǎn)生重復(fù)ID
* - 能夠根據(jù)時(shí)間戳排序
* - 每毫秒能夠生成4096個(gè)ID
*/
@Component
public class SnowflakeIdGenerator {
/**
* 起始時(shí)間戳 (2024-01-01 00:00:00)
* 可以使用約69年
*/
private static final long START_TIMESTAMP = 1704067200000L;
/**
* 數(shù)據(jù)中心ID位數(shù)
*/
private static final long DATACENTER_ID_BITS = 5L;
/**
* 機(jī)器ID位數(shù)
*/
private static final long MACHINE_ID_BITS = 5L;
/**
* 序列號位數(shù)
*/
private static final long SEQUENCE_BITS = 12L;
/**
* 數(shù)據(jù)中心ID最大值 (2^5 - 1 = 31)
*/
private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
/**
* 機(jī)器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);
/**
* 機(jī)器ID左移位數(shù)
*/
private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;
/**
* 數(shù)據(jù)中心ID左移位數(shù)
*/
private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
/**
* 時(shí)間戳左移位數(shù)
*/
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS + DATACENTER_ID_BITS;
/**
* 數(shù)據(jù)中心ID
*/
private final long datacenterId;
/**
* 機(jī)器ID
*/
private final long machineId;
/**
* 序列號
*/
private long sequence = 0L;
/**
* 上次生成ID的時(shí)間戳
*/
private long lastTimestamp = -1L;
/**
* 構(gòu)造函數(shù)
*/
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("數(shù)據(jù)中心ID必須在0-%d之間", MAX_DATACENTER_ID));
}
if (machineId > MAX_MACHINE_ID || machineId < 0) {
throw new IllegalArgumentException(
String.format("機(jī)器ID必須在0-%d之間", MAX_MACHINE_ID));
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
/**
* 生成下一個(gè)ID
*
* @return 唯一ID
*/
public synchronized long nextId() {
long timestamp = getCurrentTimestamp();
// 如果當(dāng)前時(shí)間小于上一次ID生成的時(shí)間戳,說明系統(tǒng)時(shí)鐘回退過,拋出異常
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("系統(tǒng)時(shí)鐘回退,拒絕生成ID。時(shí)鐘回退了%d毫秒", lastTimestamp - timestamp));
}
// 如果是同一時(shí)間生成的,則進(jìn)行毫秒內(nèi)序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
// 毫秒內(nèi)序列溢出,等待下一毫秒
if (sequence == 0) {
timestamp = getNextTimestamp(lastTimestamp);
}
} else {
// 時(shí)間戳改變,毫秒內(nèi)序列重置
sequence = 0L;
}
// 上次生成ID的時(shí)間戳
lastTimestamp = timestamp;
// 移位并通過或運(yùn)算拼到一起組成64位的ID
return ((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獲取生成時(shí)間
*
* @param id 雪花ID
* @return 生成時(shí)間戳
*/
public long parseTimestamp(long id) {
return (id >> TIMESTAMP_SHIFT) + START_TIMESTAMP;
}
/**
* 解析ID獲取數(shù)據(jù)中心ID
*
* @param id 雪花ID
* @return 數(shù)據(jù)中心ID
*/
public long parseDatacenterId(long id) {
return (id >> DATACENTER_ID_SHIFT) & MAX_DATACENTER_ID;
}
/**
* 解析ID獲取機(jī)器ID
*
* @param id 雪花ID
* @return 機(jī)器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;
}
/**
* 獲取當(dāng)前時(shí)間戳
*
* @return 當(dāng)前時(shí)間戳
*/
private long getCurrentTimestamp() {
return System.currentTimeMillis();
}
/**
* 獲取下一毫秒時(shí)間戳
*
* @param lastTimestamp 上次時(shí)間戳
* @return 下一毫秒時(shí)間戳
*/
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生成工具類
* 提供靜態(tài)方法方便調(diào)用
*/
@Component
public class IdUtils {
@Autowired
private SnowflakeIdGenerator snowflakeIdGenerator;
private static SnowflakeIdGenerator staticSnowflakeIdGenerator;
// 靜態(tài)初始化,確保在沒有Spring容器時(shí)也能工作
static {
if (staticSnowflakeIdGenerator == null) {
staticSnowflakeIdGenerator = new SnowflakeIdGenerator(1, 1);
}
}
@PostConstruct
public 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獲取生成時(shí)間
*
* @param id 雪花ID
* @return 生成時(shí)間戳
*/
public static long parseTimestamp(long id) {
return staticSnowflakeIdGenerator.parseTimestamp(id);
}
/**
* 解析ID獲取數(shù)據(jù)中心ID
*
* @param id 雪花ID
* @return 數(shù)據(jù)中心ID
*/
public static long parseDatacenterId(long id) {
return staticSnowflakeIdGenerator.parseDatacenterId(id);
}
/**
* 解析ID獲取機(jī)器ID
*
* @param id 雪花ID
* @return 機(jī)器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;
/**
* 簡單的雪花算法測試工具
* 獨(dú)立運(yùn)行,不依賴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");
// 創(chuàng)建生成器實(shí)例
SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);
// 顯示配置信息
showInfo(generator);
// 基礎(chǔ)功能測試
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(" 數(shù)據(jù)中心ID: 1");
System.out.println(" 機(jī)器ID: 1");
System.out.println(" 當(dāng)前時(shí)間: " + LocalDateTime.now().format(FORMATTER));
System.out.println();
}
/**
* 基礎(chǔ)ID生成測試
*/
private static void testBasicGeneration(SnowflakeIdGenerator generator) {
System.out.println("?? 基礎(chǔ)ID生成測試:");
// 生成單個(gè)ID
long id1 = generator.nextId();
System.out.println(" 單個(gè)ID: " + id1);
// 連續(xù)生成幾個(gè)ID
System.out.println(" 連續(xù)生成5個(gè)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 + " 個(gè)ID耗時(shí): " + (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 + " 個(gè)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(" 總耗時(shí): " + duration + "ms");
System.out.println(" 平均每個(gè)ID: " + String.format("%.4f", avgTimePerId) + "ms");
System.out.println(" 每秒生成: " + String.format("%.0f", idsPerSecond) + " 個(gè)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數(shù)量: " + testCount);
System.out.println(" 唯一ID數(shù)量: " + ids.size());
System.out.println(" 唯一性測試: " + (ids.size() == testCount ? "? 通過" : "? 失敗"));
System.out.println(" 測試耗時(shí): " + (endTime - startTime) + "ms");
System.out.println();
}
/**
* 遞增性測試
*/
private static void testIncrement(SnowflakeIdGenerator generator) {
System.out.println("?? 遞增性測試:");
int testCount = 100;
List<Long> ids = new ArrayList<>();
// 生成測試ID
for (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數(shù)量: " + testCount);
System.out.println(" 遞增性測試: " + (isIncreasing ? "? 通過" : "? 失敗"));
if (!isIncreasing) {
System.out.println(" 非遞增數(shù)量: " + nonIncreasingCount);
}
// 顯示前幾個(gè)和后幾個(gè)ID
System.out.println(" 前5個(gè)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個(gè)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解析測試:");
// 創(chuàng)建生成器
SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);
Long testId = generator.nextId();
System.out.println(" 測試ID: " + testId);
// 解析ID的各個(gè)部分
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(" 解析結(jié)果:");
System.out.println(" 時(shí)間戳: " + timestamp);
System.out.println(" 生成時(shí)間: " + generatedTime.format(FORMATTER));
System.out.println(" 數(shù)據(jù)中心ID: " + datacenterId);
System.out.println(" 機(jī)器ID: " + machineId);
System.out.println(" 序列號: " + sequence);
// 計(jì)算ID生成到現(xiàn)在的時(shí)間差
long age = System.currentTimeMillis() - timestamp;
System.out.println(" ID年齡: " + age + "ms");
System.out.println();
}
/**
* 演示不同配置的生成器
*/
private static void testDifferentConfigurations() {
System.out.println("?? 不同配置測試:");
// 創(chuàng)建不同配置的生成器
SnowflakeIdGenerator generator1 = new SnowflakeIdGenerator(1, 1);
SnowflakeIdGenerator generator2 = new SnowflakeIdGenerator(1, 2);
SnowflakeIdGenerator generator3 = new SnowflakeIdGenerator(2, 1);
System.out.println(" 數(shù)據(jù)中心1-機(jī)器1: " + generator1.nextId());
System.out.println(" 數(shù)據(jù)中心1-機(jī)器2: " + generator2.nextId());
System.out.println(" 數(shù)據(jù)中心2-機(jī)器1: " + generator3.nextId());
System.out.println();
}
}
直接運(yùn)行



4. 在業(yè)務(wù)中使用
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的生成和解析服務(wù)
*/
@RestController
@RequestMapping("/common/id")
@Api(tags = "ID生成服務(wù)")
public class IdController {
@Autowired
private SnowflakeIdGenerator snowflakeIdGenerator;
/**
* 生成單個(gè)雪花算法ID
*/
@GetMapping("/generate")
@ApiOperation("生成單個(gè)雪花算法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("生成數(shù)量,最大100") @RequestParam(defaultValue = "10") int count) {
if (count <= 0 || count > 100) {
return ApiResponse.error(400, "生成數(shù)量必須在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);
// 計(jì)算ID生成到現(xiàn)在的時(shí)間差
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個(gè)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();
// 生成一個(gè)示例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個(gè)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;
// 驗(yàn)證ID的唯一性
long distinctCount = testIds.stream().distinct().count();
boolean isUnique = distinctCount == testIds.size();
// 驗(yàn)證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);
}
}
}
啟動(dòng)項(xiàng)目后,訪問:
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
}就能得到一個(gè) 分布式唯一 ID。
四、雪花算法的優(yōu)缺點(diǎn)
? 優(yōu)點(diǎn)
- 本地生成 ID,高性能、低延遲。
- 64 位 long 型,適合數(shù)據(jù)庫主鍵,索引效率高。
- 趨勢遞增,分庫分表更友好。
?? 缺點(diǎn)
- 時(shí)間回?fù)軉栴}:如果系統(tǒng)時(shí)間被調(diào)回,可能導(dǎo)致 ID 重復(fù)。
- workerId 配置:需要保證每臺機(jī)器的 workerId 唯一,否則會(huì)沖突。
- 單機(jī)生成上限:每毫秒 4096 個(gè) ID,極端場景可能不夠。
五、適用場景
- 用戶 ID、訂單號、消息 ID 等需要分布式唯一 ID 的場景。
- 高并發(fā)系統(tǒng)中需要性能高、趨勢遞增的 ID。
- 分庫分表需要按 ID 范圍路由的系統(tǒng)。
六、分布式唯一 ID 生成方案對比
在分布式系統(tǒng)中,唯一 ID 是最基礎(chǔ)的需求。常見的實(shí)現(xiàn)方式有以下幾類:
1. 數(shù)據(jù)庫自增 ID
原理:依賴數(shù)據(jù)庫主鍵自增特性(AUTO_INCREMENT / Sequence)。
優(yōu)點(diǎn):
- 實(shí)現(xiàn)簡單,無需額外服務(wù)。
- ID 有序,方便分頁與索引。
缺點(diǎn):
- 單庫有性能瓶頸,QPS 不高。
- 擴(kuò)展到分布式場景需要分庫分表,增加復(fù)雜性。
適用場景:小型項(xiàng)目、單體應(yīng)用。
2. UUID / GUID
原理:基于隨機(jī)數(shù)、MAC 地址和時(shí)間戳生成 128 位 ID。
優(yōu)點(diǎn):
- 本地生成,無需依賴第三方服務(wù)。
- 全球唯一,沖突概率極低。
缺點(diǎn):
- 長度過長(16 字節(jié) / 36 字符),存儲(chǔ)和索引性能差。
- 無序,不利于數(shù)據(jù)庫分頁與索引。
適用場景:日志追蹤、分布式追蹤、無需排序的業(yè)務(wù)。
3. Redis INCR
原理:利用 Redis 的原子自增操作生成 ID。
優(yōu)點(diǎn):
- 高并發(fā)下性能優(yōu)秀。
- 簡單易用,支持多節(jié)點(diǎn)共享。
缺點(diǎn):
- 依賴 Redis,高可用需額外維護(hù)。
- 擴(kuò)展性依賴 Redis 集群。
適用場景:電商訂單號、消息序列號。
4. Twitter Snowflake
原理:使用 64 位二進(jìn)制結(jié)構(gòu)(時(shí)間戳 + 數(shù)據(jù)中心 ID + 機(jī)器 ID + 序列號)。
優(yōu)點(diǎn):
- 高性能,本地生成,不依賴數(shù)據(jù)庫。
- 數(shù)字型,長度適中,遞增趨勢。
缺點(diǎn):
- 需要合理分配數(shù)據(jù)中心和機(jī)器 ID。
- 對系統(tǒng)時(shí)鐘依賴強(qiáng),時(shí)鐘回?fù)軙?huì)導(dǎo)致重復(fù) ID。
適用場景:高并發(fā)系統(tǒng)(訂單 ID、日志 ID、分布式唯一標(biāo)識)。
5. Zookeeper 分布式 ID
原理:利用 Zookeeper 順序節(jié)點(diǎn)特性生成 ID。
優(yōu)點(diǎn):
- 強(qiáng)一致性,保證全局唯一遞增。
缺點(diǎn):
- 性能一般,QPS 不高。
- 依賴 Zookeeper 集群,運(yùn)維成本高。
適用場景:金融業(yè)務(wù)、分布式鎖、強(qiáng)一致性場景。
6. 百度 UidGenerator
原理:基于 Snowflake 改造,支持時(shí)間回?fù)芴幚?,依賴?shù)據(jù)庫分配 WorkerID。
優(yōu)點(diǎn):
- 高性能,單機(jī) QPS 可達(dá) 600 萬。
- 支持秒級、分級、時(shí)級時(shí)間單位,靈活。
缺點(diǎn):
- 依賴數(shù)據(jù)庫分配 WorkerID。
- 社區(qū)活躍度一般,維護(hù)較少。
適用場景:高并發(fā)業(yè)務(wù)(訂單、交易流水、日志 ID)。
7. 美團(tuán) Leaf
原理:提供兩種模式:
- Segment 模式:基于數(shù)據(jù)庫號段。
- Snowflake 模式:本地生成。
優(yōu)點(diǎn):
- 雙模式保證高可用(DB + Snowflake)。
- 經(jīng)過美團(tuán)生產(chǎn)驗(yàn)證,穩(wěn)定可靠。
- Leaf-Snowflake 支持 ZooKeeper 進(jìn)行 WorkerID 分配,避免沖突。
缺點(diǎn):
- 系統(tǒng)復(fù)雜度較高。
- 部署依賴 ZooKeeper 或數(shù)據(jù)庫。
適用場景:大規(guī)模分布式系統(tǒng)(訂單 ID、交易號、日志流水號)。
總結(jié)對比表
| 方案 | 長度 | 順序性 | 依賴組件 | 性能 (QPS) | 復(fù)雜度 | 適用場景 |
|---|---|---|---|---|---|---|
| 數(shù)據(jù)庫自增 ID | 短 | 有序 | 數(shù)據(jù)庫 | 低 | 低 | 小型項(xiàng)目 |
| UUID / GUID | 長 | 無序 | 無 | 高 | 低 | 日志追蹤 |
| Redis INCR | 短 | 有序 | Redis | 高 | 中 | 電商訂單 |
| Snowflake | 中 | 趨勢有序 | 無 | 高 | 中 | 高并發(fā)系統(tǒng) |
| Zookeeper 順序 ID | 短 | 有序 | ZK | 中 | 高 | 金融業(yè)務(wù) |
| 百度 UidGenerator | 中 | 有序 | DB | 極高 | 中 | 高并發(fā)場景 |
| 美團(tuán) Leaf | 中 | 有序 | DB/ZK | 極高 | 高 | 分布式系統(tǒng) |
?? 總結(jié):
- 小型項(xiàng)目:用數(shù)據(jù)庫自增即可。
- 日志、追蹤:UUID 最方便。
- 高并發(fā)但輕依賴:Twitter Snowflake / 百度 UidGenerator。
- 大規(guī)模分布式系統(tǒng):美團(tuán) Leaf 最優(yōu)(生產(chǎn)驗(yàn)證,雙模式保障)。
七、總結(jié)
本文介紹了 雪花算法的原理,并結(jié)合 Spring Boot 實(shí)現(xiàn)了一個(gè) 分布式唯一 ID 生成器。
相比 UUID,雪花算法生成的 ID 更短、更高效,適合作為數(shù)據(jù)庫主鍵。
到此這篇關(guān)于SpringBoot集成雪花算法唯一ID的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot雪花算法唯一ID內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)Rabbitmq延遲隊(duì)列和惰性隊(duì)列
本文主要介紹了java實(shí)現(xiàn)Rabbitmq延遲隊(duì)列和惰性隊(duì)列,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12
springboot整合mybatis實(shí)現(xiàn)數(shù)據(jù)庫的更新批處理方式
這篇文章主要介紹了springboot整合mybatis實(shí)現(xiàn)數(shù)據(jù)庫的更新批處理方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
詳解Java數(shù)據(jù)庫連接JDBC基礎(chǔ)知識(操作數(shù)據(jù)庫:增刪改查)
這篇文章主要介紹了詳解Java數(shù)據(jù)庫連接JDBC基礎(chǔ)知識(操作數(shù)據(jù)庫:增刪改查),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
Mybatis實(shí)現(xiàn)傳入多個(gè)參數(shù)的四種方法詳細(xì)講解
這篇文章主要介紹了Mybatis實(shí)現(xiàn)傳入多個(gè)參數(shù)的四種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01
springboot3使用redis 最新詳細(xì)方法示例詳解
本文介紹Spring Boot 3中使用Redis的配置方法與數(shù)據(jù)結(jié)構(gòu)操作,涵蓋String、List、Set、ZSet、Hash、Bitmap等類型的應(yīng)用場景,如緩存、消息隊(duì)列、排行榜等,提供高效數(shù)據(jù)處理方案,感興趣的朋友跟隨小編一起看看吧2025-07-07
Java中正則表達(dá)式的語法以及matches方法的使用方法
正則表達(dá)式(Regular Expression)是一門簡單語言的語法規(guī)范,是強(qiáng)大、便捷、高效的文本處理工具,這篇文章主要給大家介紹了關(guān)于Java中正則表達(dá)式的語法以及matches方法的使用方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-05-05
解決HttpServletResponse和HttpServletRequest取值的2個(gè)坑
這篇文章主要介紹了解決HttpServletResponse和HttpServletRequest取值的2個(gè)坑問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
Spring?Cloud?Gateway集成Sentinel流控詳情
這篇文章主要介紹了Spring?Cloud?Gateway集成Sentinel流控詳情,Sentinel支持對Spring?Cloud?Gateway、Zuul等主流的API?Gateway進(jìn)行限流,需要的朋友可以參考一下2022-09-09

