一种新的分布式ID生成方案--ULID
一种新的分布式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) {// 生成一个ULIDUlid 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);// 从字符串解析ULIDUlid 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次生成)时理论上可能冲突,但实际几乎不可能