一种新的分布式ID生成方案
ULID: 一种新的分布式ID生成方案
ULID (Universally Unique Lexicographically Sortable Identifier) 是一种较新的分布式ID生成方案,旨在解决传统UUID和雪花算法(Snowflake)的一些局限性。
ULID的主要特点
可排序性:ULID按生成时间严格排序,便于数据库索引优化
128位兼容性:与UUID相同的128位长度(26字符Base32编码)
无特殊字符:仅使用Base32编码(字母A-Z和数字2-7)
跨语言支持:多种编程语言实现可用
单调递增:同一毫秒内生成的ULID也能保持顺序
ULID结构
适用场景
需要按时间排序的数据库主键
分布式系统需要无协调的ID生成
需要人类可读但不想暴露内部信息的ID
替换UUIDv4但需要排序能力的场景
各语言实现示例
javascript
// Node.js
const { ULID } = require('ulid');
const id = ULID(); // 01H5Z7K0G2ABC123DEF456GHJ
Python
import ulid
id = ulid.new() # 01H5Z7K0G2ABC123DEF456GHJ
Java
// Java
import de.huxhorn.sulky.ulid.ULID;
ULID ulid = new ULID();
String id = ulid.nextULID(); // 01H5Z7K0G2ABC123DEF456GHJ
优缺点分析
优点:
无需中心化协调器
比UUID更友好的排序和索引性能
比Snowflake更简单的实现
时间信息可提取(前10字符代表时间)
缺点:
随机部分不如UUIDv4随机(时间部分固定)
相对较新,生态支持不如UUID广泛
80位随机性理论上存在冲突可能(但极低)
ULID为分布式系统ID生成提供了一个平衡了排序性、唯一性和易用性的新选择,特别适合需要时间排序的场景。
1. 添加依赖
首先添加 ULID 的 Java 实现库到你的项目中:
Maven 依赖
<dependency>
<groupId>com.github.f4b6a3</groupId>
<artifactId>ulid-creator</artifactId>
<version>5.2.0</version> <!-- 检查最新版本 -->
</dependency>
2. 基本使用方法
import com.github.f4b6a3.ulid.Ulid;
import com.github.f4b6a3.ulid.UlidCreator;
public class UlidExample {
public static void main(String[] args) {
// 生成一个ULID
Ulid ulid = UlidCreator.getUlid();
System.out.println("ULID: " + ulid); // 例如: 01H5Z7K0G2ABC123DEF456GHJ
// 获取ULID的不同部分
System.out.println("Timestamp: " + ulid.getTimestamp()); // 48位时间戳
System.out.println("Random: " + ulid.getRandom()); // 80位随机部分
// 获取字符串表示
String ulidString = ulid.toString();
System.out.println("String: " + ulidString);
// 从字符串解析ULID
Ulid parsedUlid = Ulid.from(ulidString);
System.out.println("Parsed: " + parsedUlid);
}
}
3. 高级用法
单调递增ULID (同一毫秒内有序)
// 创建单调递增的ULID生成器
UlidCreator.Monotonic ulidMonotonic = UlidCreator.getMonotonicUlid();
// 同一毫秒内生成的ULID会保持顺序
Ulid ulid1 = ulidMonotonic.create();
Ulid ulid2 = ulidMonotonic.create();
System.out.println(ulid1.compareTo(ulid2) < 0); // 输出 true
自定义随机数生成器
import java.security.SecureRandom;
// 使用更安全的随机数生成器
SecureRandom secureRandom = new SecureRandom();
Ulid ulid = UlidCreator.getUlid(secureRandom);
获取时间信息
import java.time.Instant;
Ulid ulid = UlidCreator.getUlid();
Instant instant = ulid.getInstant(); // 获取生成时间
System.out.println("Created at: " + instant);
4. 性能优化
对于高性能场景,可以使用更高效的方法:
// 生成ULID并直接获取字节数组
byte[] bytes = new byte[16];
UlidCreator.getUlid(bytes); // 填充16字节数组
// 从字节数组创建ULID
Ulid fromBytes = Ulid.from(bytes);
5. 与UUID互转
import java.util.UUID;
// ULID转UUID
Ulid ulid = UlidCreator.getUlid();
UUID uuid = ulid.toUuid();
// UUID转ULID
Ulid fromUuid = Ulid.from(uuid);
6. 完整工具类示例
import com.github.f4b6a3.ulid.Ulid;
import com.github.f4b6a3.ulid.UlidCreator;
import java.time.Instant;
import java.util.UUID;
public class UlidUtils {
/**
* 生成标准ULID
*/
public static String generate() {
return UlidCreator.getUlid().toString();
}
/**
* 生成单调递增ULID
*/
public static String generateMonotonic() {
return UlidCreator.getMonotonicUlid().create().toString();
}
/**
* 获取ULID的生成时间
*/
public static Instant getInstant(String ulid) {
return Ulid.from(ulid).getInstant();
}
/**
* ULID转UUID
*/
public static UUID toUuid(String ulid) {
return Ulid.from(ulid).toUuid();
}
/**
* UUID转ULID
*/
public static String fromUuid(UUID uuid) {
return Ulid.from(uuid).toString();
}
/**
* 验证字符串是否为有效ULID
*/
public static boolean isValid(String ulid) {
try {
Ulid.from(ulid);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
}
7. 性能考虑
基准测试:在常规硬件上,ULID生成速度可达每秒数百万次
线程安全:UlidCreator是线程安全的
无阻塞:实现不依赖网络或IO操作
8. 注意事项
ULID区分大小写,但标准实现通常使用大写字母
时间戳部分基于UNIX时间戳(毫秒),可表示到10889年
随机部分使用安全的随机数生成器
在极高并发(同一毫秒内超过2^80次生成)时理论上可能冲突,但实际几乎不可能