全新分布式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生成解决方案。

相关推荐
乌恩大侠2 小时前
【AI-RAN 调研】软银株式会社的 “AITRAS” 基于 Arm 架构的 NVIDIA 平台 实现 集中式与分布式 AI-RAN 架构
人工智能·分布式·fpga开发·架构·usrp·mimo
alonewolf_992 小时前
ZooKeeper ZAB协议源码深度剖析:从理论到实践的分布式一致性指南
分布式·zookeeper
机灵猫14 小时前
Redisson 到底能做什么?从分布式锁说起
分布式
U-Mail邮件系统20 小时前
U-Mail企业邮件系统分布式部署方案
分布式
鱼跃鹰飞1 天前
面试题:Kafka的零拷贝的底层实现是什么?是MMAP还是sendFile还是其他的?
分布式·kafka·系统架构
工业甲酰苯胺1 天前
【面试题】RabbitMQ 中无法路由的消息会去到哪里?
分布式·rabbitmq
weixin_457297101 天前
Hadoop面试题
大数据·hadoop·分布式
何亚告1 天前
记一次项目上hadoop数据迁移
大数据·hadoop·分布式
少云清1 天前
【性能测试】13_JMeter _JMeter分布式
分布式·jmeter·性能测试