全新分布式ID组件TSID支持N种数据类型

TSID:时间排序唯一标识符的完整指南

1. 简介

标准的128位随机UUID虽然流行,但在数据库主键应用中存在显著缺陷:

UUID作为主键的问题:

  • 存储空间大:每条记录需要16字节,影响外键列存储
  • 索引效率低 :B+树索引随机值导致:
    • 填充因子低:8kB页只存储少量元素,浪费磁盘和内存
    • 频繁页分裂:随机插入导致索引频繁重新平衡
    • 缓存效率差:随机访问模式降低缓冲池效果

2. TSID核心概念

2.1 什么是TSID?

TSID(Time-Sorted Unique Identifier)融合了Twitter Snowflake和ULID思想:

  • 时间排序:按生成时间自然排序
  • 紧凑存储:64位整数或13字符字符串
  • Base32编码:采用Crockford编码,URL安全、无大小写敏感
  • 更短长度:比UUID、ULID、KSUID更短

2.2 TSID结构

复制代码
42位时间戳 + 22位随机部分
  • 时间部分(42位):自2020-01-01 00:00:00 UTC以来的毫秒数
  • 随机部分 (22位):
    • 节点标识符:0-20位(默认10位)
    • 计数器:2-22位(取决于节点位)
配置示例(默认):
  • 节点位:10位 → 最大节点值:2¹⁰-1 = 1023
  • 计数位:12位 → 最大计数值:2¹²-1 = 4095
  • 每毫秒最大TSID数:4096

2.3 基本使用

java 复制代码
// 1. 创建TSID对象
TSID tsid = TSID.Factory.getTsid();

// 2. 生成Long类型ID
long id = TSID.Factory.getTsid().toLong();  // 输出:797263809025044592

// 3. 生成String类型ID
String strId = TSID.Factory.getTsid().toString();  // 输出:0P43KE5EXXM3Z

// 4. 批量生成
for (int i = 0; i < 20; i++) {
    long id = TSID.Factory.getTsid().toLong();
    System.out.println(id);
}

// 5. 快速生成(每毫秒最多4,194,304个)
TSID fastTsid = TSID.fast();

// 6. 从字符串恢复
TSID tsid = TSID.from("0123456789ABC");
System.out.println(tsid.toLong());  // 输出:38390726480144748

// 7. 获取创建时间戳
Instant instant = TSID.Factory.getTsid().getInstant();
System.out.println(instant);  // 输出:2026-01-01T01:03:34.735Z

// 8. Base-62编码
String base62 = TSID.Factory.getTsid().encode(62);
System.out.println(base62);  // 输出:0wtVRVNVMwu

// 9. 格式化输出
String formatted = tsid.format("pack%S");
System.out.println(formatted);  // 输出:pack0P43SMJ0FH80H

2.4 密钥生成器实现

java 复制代码
public class KeyGenerator {
    public static String next() {
        return TSID.Factory.getTsid().toString();
    }
}

// 使用
KeyGenerator.next();  // 0P43T4Z0VAC3S
KeyGenerator.next();  // 0P43T4Z0VAC3T
KeyGenerator.next();  // 0P43T4Z0VAC3V

2.5 模拟Twitter Snowflake

java 复制代码
// Twitter Snowflake配置
int datacenter = 1;  // 最大值:31 (2⁵-1)
int worker = 1;      // 最大值:31 (2⁵-1)
int node = (datacenter << 5 | worker);  // 最大值:1023 (2¹⁰-1)

Instant customEpoch = Instant.ofEpochMilli(1288834974657L);
IntFunction<byte[]> randomFunction = (x) -> new byte[x];

TSID.Factory factory = TSID.Factory.builder()
    .withRandomFunction(randomFunction)
    .withCustomEpoch(customEpoch)
    .withNode(node)
    .build();

TSID tsid = factory.generate();
for (int i = 0; i < 5; i++) {
    System.out.println(tsid.toLong());
}

3. JPA集成示例

3.1 TSID组件配置

java 复制代码
@Component
public class TsidComponent {
    public static final String TSID_NODE_COUNT_PROPERTY = "tsid.node.count";
    public static final String TSID_NODE_COUNT_ENV = "TSID_NODE_COUNT";
    public static TSID.Factory TSID_FACTORY;
    
    static {
        String nodeCountSetting = System.getProperty(TSID_NODE_COUNT_PROPERTY);
        if (nodeCountSetting == null) {
            nodeCountSetting = System.getenv(TSID_NODE_COUNT_ENV);
        }
        int nodeCount = nodeCountSetting != null ? 
            Integer.parseInt(nodeCountSetting) : 256;
        TSID_FACTORY = getTsidFactory(nodeCount);
    }
    
    public static TSID.Factory getTsidFactory(int nodeCount, int nodeId) {
        int nodeBits = ((int) (Math.log(nodeCount) / Math.log(2))) + 1;
        return TSID.Factory.builder()
            .withRandomFunction(TSID.Factory.THREAD_LOCAL_RANDOM_FUNCTION)
            .withNodeBits(nodeBits)
            .withNode(nodeId)
            .build();
    }
}

3.2 自定义ID生成器注解

java 复制代码
// ID生成器类型注解
@IdGeneratorType(PackIdGenerator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.FIELD })
public @interface PackSequence {
}

// 实体类使用
@Entity
@Table(name = "o_student")
public class Student {
    @Id
    @PackSequence
    private Long id;
    
    private String name;
    private String sno;
    
    // getters and setters
}

3.3 测试示例

java 复制代码
@Resource
private StudentService studentService;

@Test
public void testSave() {
    for (int i = 0; i < 10; i++) {
        Student student = new Student();
        student.setName("pack_xg_%s".formatted(i));
        student.setSno("X00002-%s".formatted(i));
        this.studentService.save(student);
    }
}

4. TSID优势总结

特性 UUID 自增ID TSID
排序性 随机无序 单库有序 时间有序
分布式 支持 不支持 完美支持
存储空间 16字节 通常8字节 8字节
索引性能 差(随机插入) 好(顺序插入) 优秀
可读性 差(长字符串) 好(数字)
冲突概率 极低 无(单库) 极低

5. 适用场景

  1. 微服务架构:分布式系统需要全局唯一ID
  2. 数据库分片:跨多个数据库实例保持ID唯一性
  3. 时间序列数据:日志、事件流等按时间排序的数据
  4. 高并发系统:需要高性能ID生成的场景
  5. 替代UUID:需要保持唯一性但提升数据库性能的场景

6. 注意事项

  1. 时间回拨:系统时间回拨可能导致ID冲突,TSID内置了时间保护机制
  2. 节点配置:在生产环境中合理配置节点ID,避免冲突
  3. 安全性 :对于安全敏感场景,使用Factory.getTsid()而非fast()
  4. 迁移成本:从现有ID方案迁移需要考虑数据迁移和兼容性

TSID为现代分布式系统提供了一个高性能、可扩展且数据库友好的ID生成解决方案。

相关推荐
ACP广源盛139246256733 小时前
GSV5600@ACP#多接口协议转换芯片,物理 AI 便携终端的互联核心
大数据·人工智能·分布式·嵌入式硬件·spark
极客先躯8 小时前
高级java每日一道面试题-2026年02月12日-实战篇[Docker]-什么是容器的 Seccomp 配置?如何自定义?
java·运维·分布式·docker·容器·自动化·文件
Francek Chen8 小时前
【大数据处理与分析】MapReduce:06 MapReduce编程实践
大数据·hadoop·分布式·mapreduce
小马爱打代码8 小时前
Kafka消息队列监控:Topic积压、吞吐量、Broker负载及消费者组全观测
分布式·kafka
轻口味8 小时前
轻规划鸿蒙开发实战10:分布式数据同步深度博弈,UserId 隔离与并发数据冲突消解机
分布式·华为·harmonyos·鸿蒙
Solis程序员9 小时前
Raft:分布式系统的定海神针
java·分布式·kafka·rabbitmq·agent·raft
我是一颗柠檬9 小时前
【Java项目技术亮点】Leaf号段模式双Buffer优化
java·开发语言·分布式·后端·架构
芒鸽9 小时前
HarmonyOS 分布式开发实战:设备协同、数据共享与跨设备迁移
分布式·wpf·harmonyos
省四收割者9 小时前
从硬件中断到分布式协程:全景解构高并发机制与 C / Golang 的巅峰对决
c++·分布式·嵌入式硬件·golang
知识分享小能手9 小时前
Hadoop学习教程,从入门到精通, HBase 分布式数据库 — 完整知识点与案例代码(8)
数据库·hadoop·分布式