文章目录
-
- [1. 项目概述](#1. 项目概述)
-
- [1.1 什么是 CosId?](#1.1 什么是 CosId?)
- [1.2 核心特性一览](#1.2 核心特性一览)
- [1.3 四大 ID 生成算法对比](#1.3 四大 ID 生成算法对比)
- [2. 为什么需要分布式 ID](#2. 为什么需要分布式 ID)
-
- [2.1 背景:从单体到分布式的挑战](#2.1 背景:从单体到分布式的挑战)
- [2.2 分布式 ID 的六大核心指标](#2.2 分布式 ID 的六大核心指标)
- [2.3 主流方案对比](#2.3 主流方案对比)
- [3. 核心架构与设计理念](#3. 核心架构与设计理念)
-
- [3.1 整体架构图](#3.1 整体架构图)
- [3.2 设计哲学](#3.2 设计哲学)
- [3.3 模块结构](#3.3 模块结构)
- [4. 环境准备与安装](#4. 环境准备与安装)
-
- [4.1 版本兼容性](#4.1 版本兼容性)
- [4.2 Gradle 构建(Kotlin DSL)](#4.2 Gradle 构建(Kotlin DSL))
- [4.3 Maven 构建](#4.3 Maven 构建)
- [4.4 基础设施依赖](#4.4 基础设施依赖)
- [5. 快速开始(5 分钟上手)](#5. 快速开始(5 分钟上手))
-
- [5.1 最简配置示例(Redis 分发器)](#5.1 最简配置示例(Redis 分发器))
- [5.2 在代码中使用](#5.2 在代码中使用)
- [5.3 运行验证](#5.3 运行验证)
- [5.4 ⚠️ 重要注意事项](#5.4 ⚠️ 重要注意事项)
- [6. CosIdGenerator 详解](#6. CosIdGenerator 详解)
-
- [6.1 什么是 CosIdGenerator?](#6.1 什么是 CosIdGenerator?)
- [6.2 位分配结构](#6.2 位分配结构)
-
- [Base36 格式(默认)](#Base36 格式(默认))
- [Base62 格式](#Base62 格式)
- [6.3 配置示例](#6.3 配置示例)
- [6.4 适用场景](#6.4 适用场景)
- [7. SnowflakeId 雪花算法详解](#7. SnowflakeId 雪花算法详解)
-
- [7.1 算法原理](#7.1 算法原理)
- [7.2 各字段含义与容量](#7.2 各字段含义与容量)
- [7.3 设计优缺点分析](#7.3 设计优缺点分析)
-
- [✅ 优点](#✅ 优点)
- [❌ 缺点与挑战](#❌ 缺点与挑战)
- [7.4 CosId 对 SnowflakeId 的增强](#7.4 CosId 对 SnowflakeId 的增强)
- [7.5 两种时间精度实现](#7.5 两种时间精度实现)
- [7.6 SnowflakeFriendlyId(友好型 ID)](#7.6 SnowflakeFriendlyId(友好型 ID))
- [7.7 SafeJavaScriptSnowflakeId(JS 安全型)](#7.7 SafeJavaScriptSnowflakeId(JS 安全型))
- [7.8 ClockSyncSnowflakeId(时钟同步包装器)](#7.8 ClockSyncSnowflakeId(时钟同步包装器))
- [7.9 SnowflakeId 完整配置](#7.9 SnowflakeId 完整配置)
- [8. SegmentId 号段模式详解](#8. SegmentId 号段模式详解)
-
- [8.1 算法原理](#8.1 算法原理)
- [8.2 有序性特征](#8.2 有序性特征)
- [8.3 IdSegmentDistributor 接口](#8.3 IdSegmentDistributor 接口)
- [8.4 四种号段分发器实现](#8.4 四种号段分发器实现)
- [8.5 JDBC 分发器初始化](#8.5 JDBC 分发器初始化)
- [8.6 GroupedIdSegmentDistributor(分组分发器)](#8.6 GroupedIdSegmentDistributor(分组分发器))
- [8.7 SegmentId 配置示例](#8.7 SegmentId 配置示例)
- [8.8 SegmentId 性能特征](#8.8 SegmentId 性能特征)
- [9. SegmentChainId 号段链模式(推荐)⭐](#9. SegmentChainId 号段链模式(推荐)⭐)
-
- [9.1 为什么推荐 SegmentChainId?](#9.1 为什么推荐 SegmentChainId?)
- [9.2 核心设计:PrefetchWorker(预取工作线程)](#9.2 核心设计:PrefetchWorker(预取工作线程))
- [9.3 自适应机制(Adaptability)](#9.3 自适应机制(Adaptability))
- [9.4 性能极限公式](#9.4 性能极限公式)
- [9.5 SegmentChainId 配置](#9.5 SegmentChainId 配置)
- [9.6 代码中使用 SegmentChainId](#9.6 代码中使用 SegmentChainId)
- [10. 机器号分配器 MachineIdDistributor](#10. 机器号分配器 MachineIdDistributor)
-
- [10.1 概述](#10.1 概述)
- [10.2 六种分配器实现](#10.2 六种分配器实现)
- [10.3 各分配器详细配置](#10.3 各分配器详细配置)
-
- [① ManualMachineIdDistributor(手动)](#① ManualMachineIdDistributor(手动))
- [② StatefulSetMachineIdDistributor(K8s StatefulSet)](#② StatefulSetMachineIdDistributor(K8s StatefulSet))
- [③ RedisMachineIdDistributor](#③ RedisMachineIdDistributor)
- [④ JdbcMachineIdDistributor](#④ JdbcMachineIdDistributor)
- [⑤ ZookeeperMachineIdDistributor](#⑤ ZookeeperMachineIdDistributor)
- [⑥ MongoMachineIdDistributor](#⑥ MongoMachineIdDistributor)
- [10.4 MachineState(机器状态)](#10.4 MachineState(机器状态))
- [10.5 MachineStateStorage(机器状态存储)](#10.5 MachineStateStorage(机器状态存储))
- [10.6 机器号守护(Machine Guarder)](#10.6 机器号守护(Machine Guarder))
- [11. 时钟回拨问题解决方案](#11. 时钟回拨问题解决方案)
-
- [11.1 问题背景](#11.1 问题背景)
- [11.2 两种时钟回拨场景](#11.2 两种时钟回拨场景)
- [11.3 ClockBackwardsSynchronizer(时钟回拨同步器)](#11.3 ClockBackwardsSynchronizer(时钟回拨同步器))
- [11.4 ClockSyncSnowflakeId vs 默认 SnowflakeId](#11.4 ClockSyncSnowflakeId vs 默认 SnowflakeId)
- [12. IdGeneratorProvider 多业务支持](#12. IdGeneratorProvider 多业务支持)
-
- [12.1 为什么需要 Provider?](#12.1 为什么需要 Provider?)
- [12.2 通过配置文件注册](#12.2 通过配置文件注册)
- [12.3 在代码中使用](#12.3 在代码中使用)
- [12.4 通过代码动态注册](#12.4 通过代码动态注册)
- [12.5 Bean Name 规则](#12.5 Bean Name 规则)
- [13. MyBatis 插件集成](#13. MyBatis 插件集成)
-
- [13.1 概述](#13.1 概述)
- [13.2 添加依赖](#13.2 添加依赖)
- [13.3 @CosId 注解说明](#13.3 @CosId 注解说明)
- [13.4 使用示例](#13.4 使用示例)
-
- [场景一:Long 类型 ID](#场景一:Long 类型 ID)
- [场景二:友好 ID(String 类型)](#场景二:友好 ID(String 类型))
- [场景三:Mapper 接口](#场景三:Mapper 接口)
- [13.5 使用效果](#13.5 使用效果)
- [14. ShardingSphere 分片集成](#14. ShardingSphere 分片集成)
-
- [14.1 概述](#14.1 概述)
- [14.2 CosIdKeyGenerateAlgorithm(分布式主键)](#14.2 CosIdKeyGenerateAlgorithm(分布式主键))
- [14.3 CosIdIntervalShardingAlgorithm(时间范围分片)](#14.3 CosIdIntervalShardingAlgorithm(时间范围分片))
- [14.4 CosIdModShardingAlgorithm(取模分片)](#14.4 CosIdModShardingAlgorithm(取模分片))
- [15. 完整配置参考手册](#15. 完整配置参考手册)
-
- [15.1 完整 application.yml 模板](#15.1 完整 application.yml 模板)
- [15.2 配置项速查表](#15.2 配置项速查表)
- [16. 性能基准测试](#16. 性能基准测试)
-
- [16.1 测试环境](#16.1 测试环境)
- [16.2 SnowflakeId 性能](#16.2 SnowflakeId 性能)
- [16.3 SegmentChainId 性能](#16.3 SegmentChainId 性能)
- [16.4 各算法性能汇总](#16.4 各算法性能汇总)
- [16.5 如何运行基准测试](#16.5 如何运行基准测试)
- [17. 最佳实践与生产建议](#17. 最佳实践与生产建议)
-
- [17.1 算法选择决策树](#17.1 算法选择决策树)
- [17.2 生产环境 Checklist](#17.2 生产环境 Checklist)
-
- [✅ 必做项](#✅ 必做项)
- [⚠️ 注意事项](#⚠️ 注意事项)
- [🔧 高级优化](#🔧 高级优化)
- [17.3 分发器选择指南](#17.3 分发器选择指南)
- [17.4 监控指标建议](#17.4 监控指标建议)
- [18. 常见问题 FAQ](#18. 常见问题 FAQ)
-
- [Q1: CosId 和美团 Leaf 有什么区别?](#Q1: CosId 和美团 Leaf 有什么区别?)
- [Q2: 同时开启 SnowflakeId 和 SegmentId 会怎样?](#Q2: 同时开启 SnowflakeId 和 SegmentId 会怎样?)
- [Q3: SegmentChainId 的安全距离应该设多大?](#Q3: SegmentChainId 的安全距离应该设多大?)
- [Q4: 为什么不同分发器最终都能达到相同性能?](#Q4: 为什么不同分发器最终都能达到相同性能?)
- [Q5: 如何处理 JavaScript 的 Long 溢出问题?](#Q5: 如何处理 JavaScript 的 Long 溢出问题?)
- [Q6: 时钟回拨应该如何处理?](#Q6: 时钟回拨应该如何处理?)
- [Q7: CosId 支持哪些数据库?](#Q7: CosId 支持哪些数据库?)
- [Q8: 如何在现有项目中迁移到 CosId?](#Q8: 如何在现有项目中迁移到 CosId?)
1. 项目概述
1.1 什么是 CosId?
CosId (Cosmic ID)是一个通用、灵活、高性能的分布式 ID 生成器,由 Java 全栈开发者 Ahoo-Wang 开源。它旨在为微服务架构下的分布式系统提供全局唯一的、趋势递增的高性能 ID 生成能力。
1.2 核心特性一览
| 特性 | 说明 |
|---|---|
| 🌐 通用性 | 支持多种分布式 ID 算法:SnowflakeId、SegmentId、SegmentChainId、CosIdGenerator |
| ⚡ 高性能 | SegmentChainId TPS 达到 127,439,148+ ops/s,接近 AtomicLong 性能上限 |
| 🔧 灵活性 | 位分配方案可配置、多种分发器可选、支持多业务隔离 |
| 🔌 易集成 | Spring Boot 自动配置、MyBatis 插件、ShardingSphere 插件开箱即用 |
| 🛡️ 健壮性 | 时钟回拨保护、机器号自动分配、安全距离自适应伸缩 |
1.3 四大 ID 生成算法对比
| 算法 | TPS 性能 (ops/s) | 稳定性 P9999 | 有序性 | 自治性 | 适用场景 |
|---|---|---|---|---|---|
| CosIdGenerator | 15,570,085 | --- | 全局趋势递增 | 完全自治 | 大规模集群、百万实例 |
| SnowflakeId | 4,096,000 | 0.244 us/op | 本地单调递增 | 依赖时钟 | 中小规模集群、低延迟需求 |
| SegmentId | 29,506,073 | 46.624 us/op | 本地单调递增 | 依赖第三方分发器 | 一般业务场景 |
| SegmentChainId ⭐ | 127,439,148+ | 0.208 us/op | 本地单调递增 | 依赖第三方分发器 | 高吞吐生产环境(推荐) |
2. 为什么需要分布式 ID
2.1 背景:从单体到分布式的挑战
在软件系统演进过程中,随着业务规模的增长(TPS/存储容量),我们需要通过集群化部署来分摊计算和存储压力。
┌─────────────────────────────────────────────────────┐
│ 单体应用时代 │
│ ┌──────────┐ │
│ │ 应用服务 │ ──→ ┌──────────┐ │
│ │ (单实例) │ │ 数据库 │ ← 自增主键即可 │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 分布式/微服务时代 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 实例 1 │ │ 实例 2 │ │ 实例 N │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ └──────────┼──────────┘ │
│ ▼ │
│ ┌──────────────┐ │
│ │ 分库分表数据库 │ ← 需要!分布式 ID │
│ └──────────────┘ │
└─────────────────────────────────────────────────────┘
2.2 分布式 ID 的六大核心指标
| 指标 | 说明 | 为什么重要 |
|---|---|---|
| 全局唯一性 | 相同业务不同副本生成的 ID 不冲突 | 主键约束的基本要求 |
| 有序性 | 单调递增或趋势递增 | B+树索引效率、追加写性能 |
| 吞吐量 | 单位时间生成 ID 数量 | 系统整体性能上限 |
| 稳定性 | 百分位延迟(如 P9999) | 尾部延迟影响用户体验 |
| 自治性 | 对外部中间件的依赖程度 | 影响可用性和运维复杂度 |
| 适应性 | 流量突发时的动态伸缩能力 | 应对秒杀、大促等场景 |
2.3 主流方案对比
| 方案 | 唯一性 | 有序性 | 吞吐量 | 存储空间 | 缺陷 |
|---|---|---|---|---|---|
| UUID/GUID | ✅ | ❌ 完全无序 | 307万/s | 128-bit | 索引性能差 |
| 数据库自增 | ✅ | ✅ 单调递增 | 低 | 64-bit | 扩展困难 |
| SnowflakeId | ✅ | ✅ 趋势递增 | 409万/s | 64-bit | 时钟依赖 |
| CosId SegmentChainId | ✅ | ✅ 趋势递增 | 1.27亿+/s | 64-bit | 依赖第三方分发器 |
3. 核心架构与设计理念
3.1 整体架构图
┌──────────────────────────────────────────────────────────────────┐
│ CosId 架构 │
├──────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 应用层 (Application Layer) ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ ││
│ │ │ MyBatis 插件 │ │ ShardingSphere│ │ 业务代码直接调用 │ ││
│ │ │ @CosId 注解 │ │ 分片算法集成 │ │ IdGenerator.generate│ ││
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ ││
│ └─────────────────────────────────────────────────────────────┘│
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ ID 生成引擎层 (Engine Layer) ││
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌──────────┐ ││
│ │ │CosIdGenerat│ │SnowflakeId │ │ SegmentId │ │Segment │ ││
│ │ │ or │ │ │ │ │ │ChainId │ ││
│ │ └────────────┘ └─────┬──────┘ └─────┬──────┘ └────┬─────┘ ││
│ └────────────────────────────┼────────────┼────────────┼──────┘│
│ ┌────────────────────────────▼────────────▼────────────▼──────┐│
│ │ 分发层 (Distributor Layer) ││
│ │ ┌──────────────────────────────────────────────────────┐ ││
│ │ │ MachineIdDistributor (机器号分配) │ ││
│ │ │ Manual │ StatefulSet │ Redis │ JDBC │ Zookeeper │ Mongo│ ││
│ │ └──────────────────────────────────────────────────────┘ ││
│ │ ┌──────────────────────────────────────────────────────┐ ││
│ │ │ IdSegmentDistributor (号段分发) │ ││
│ │ │ Redis │ JDBC │ Zookeeper │ MongoDB │ ││
│ │ └──────────────────────────────────────────────────────┘ ││
│ └─────────────────────────────────────────────────────────────┘│
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 存储层 (Storage Layer) ││
│ │ Redis │ MySQL/PostgreSQL │ ZooKeeper │ MongoDB ││
│ └─────────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Spring Boot 自动配置层 ││
│ │ cosid-spring-boot-starter (自动装配、属性绑定) ││
│ └─────────────────────────────────────────────────────────────┘│
└──────────────────────────────────────────────────────────────────┘
基于 cosid-core 源码结构S5 和 官方文档架构说明S2 整理
3.2 设计哲学
- 分层解耦:引擎层与分发层分离,可独立替换
- 策略模式:通过接口抽象,支持多种实现策略
- 约定优于配置:Spring Boot 自动配置,最小化用户配置量
- 渐进式复杂度:从最简配置到高级特性,按需启用
3.3 模块结构
| 模块 | 说明 |
|---|---|
cosid-core |
核心引擎:IdGenerator、SnowflakeId、SegmentId、SegmentChainId |
cosid-spring-boot-starter |
Spring Boot 自动配置、属性绑定 |
cosid-jdbc |
JDBC 号段分发器和机器号分发器 |
cosid-spring-redis |
Redis 号段分发器和机器号分发器 |
cosid-zookeeper |
ZooKeeper 号段分发器和机器号分发器 |
cosid-mongo |
MongoDB 号段分发器 |
cosid-mybatis |
MyBatis 插件(@CosId 注解) |
cosid-jackson |
Jackson 序列化支持 |
cosid-activiti |
Activiti 工作流集成 |
cosid-axon |
Axon Framework 集成 |
cosid-flowable |
Flowable 工作流集成 |
cosid-spring-data-jdbc |
Spring Data JDBC 集成 |
cosid-benchmark |
JMH 性能基准测试 |
proxy |
CosId Proxy 独立代理服务 |
4. 环境准备与安装
4.1 版本兼容性
| CosId 版本 | Spring Boot 版本 | Java 版本 | Git 分支 |
|---|---|---|---|
| 1.x | 2.x | Java 8 | 1.x branch |
| 2.x | 3.x | Java 17 | 2.x branch |
| 3.x(当前最新) | 4.x | Java 17+ | main branch |
⚠️ 重要提示 :本教程基于 CosId v3.2.0 + Spring Boot 4.x + Java 17。
4.2 Gradle 构建(Kotlin DSL)
kotlin
// build.gradle.kts
val cosidVersion = "3.2.0"
dependencies {
// 核心启动器(必选)
implementation("me.ahoo.cosid:cosid-spring-boot-starter:${cosidVersion}")
// 分发器按需选择(至少选一个)
implementation("me.ahoo.cosid:cosid-spring-redis:${cosidVersion}") // Redis
implementation("me.ahoo.cosid:cosid-jdbc:${cosidVersion}") // JDBC
implementation("me.ahoo.cosid:cosid-zookeeper:${cosidVersion}") // ZooKeeper
implementation("me.ahoo.cosid:cosid-mongo:${cosidVersion}") // MongoDB
// 可选集成
implementation("me.ahoo.cosid:cosid-mybatis:${cosidVersion}") // MyBatis 插件
}
4.3 Maven 构建
xml
<properties>
<cosid.version>3.2.0</cosid.version>
</properties>
<dependencies>
<!-- 核心启动器 -->
<dependency>
<groupId>me.ahoo.cosid</groupId>
<artifactId>cosid-spring-boot-starter</artifactId>
<version>${cosid.version}</version>
</dependency>
<!-- Redis 分发器 -->
<dependency>
<groupId>me.ahoo.cosid</groupId>
<artifactId>cosid-spring-redis</artifactId>
<version>${cosid.version}</version>
</dependency>
</dependencies>
4.4 基础设施依赖
根据选择的分发器类型,需要准备对应的基础设施:
| 分发器类型 | 所需基础设施 | 额外依赖 |
|---|---|---|
| Redis | Redis Server 3.2+ | spring-boot-starter-data-redis |
| JDBC | MySQL / PostgreSQL / Oracle | 对应数据库驱动 |
| ZooKeeper | ZooKeeper 3.5+ | spring-cloud-starter-zookeeper 或客户端 |
| MongoDB | MongoDB 3.6+ | spring-boot-starter-data-mongodb |
5. 快速开始(5 分钟上手)
5.1 最简配置示例(Redis 分发器)
yaml
# application.yml
spring:
data:
redis:
host: localhost
port: 6379
# Redis 分发器直接依赖 spring-data-redis,无需额外配置
cosid:
namespace: ${spring.application.name} # 命名空间,用于隔离不同应用
machine:
enabled: true # 启用机器号分配(雪花算法需要)
distributor:
type: redis # 使用 Redis 作为机器号分发器
snowflake:
enabled: true # 启用雪花算法 ID 生成器
segment:
enabled: true # 启用号段算法 ID 生成器
mode: chain # 使用号段链模式(推荐)
distributor:
type: redis # 使用 Redis 作为号段分发器
5.2 在代码中使用
java
@RestController
@RequestMapping("/api/demo")
public class DemoController {
/**
* 方式一:通过 @Autowired 注入共享的 IdGenerator
* 默认名称规则:
* - __share__SegmentId (号段模式)
* - __share__SnowflakeId (雪花模式)
*/
@Autowired
@Qualifier("__share__SegmentId")
private IdGenerator segmentIdGenerator;
@Autowired
@Qualifier("__share__SnowflakeId")
private SnowflakeId snowflakeId;
/**
* 方式二:通过 DefaultIdGeneratorProvider 获取
*/
@GetMapping("/id")
public Long generateId() {
// 获取共享的 ID 生成器并生成 ID
return DefaultIdGeneratorProvider.INSTANCE.getShare().generate();
}
@GetMapping("/snowflake")
public Long generateSnowflakeId() {
return snowflakeId.generate();
}
@GetMapping("/segment")
public Long generateSegmentId() {
return segmentIdGenerator.generate();
}
}
5.3 运行验证
启动应用后访问:
bash
curl http://localhost:8080/api/demo/id
# 返回: 208796080181248 (示例值)
curl http://localhost:8080/api/demo/snowflake
# 返回: 6928793728688168960 (示例值)
5.4 ⚠️ 重要注意事项
- 共享 ID 生成器冲突 :当同时开启
snowflake.enabled: true和segment.enabled: true时,只有一个共享的(__share__)IdGenerator 会注入到 Spring 容器(Bean 名称冲突),另一个会被忽略。 - 解决方案 :通过
provider配置注册多个命名 ID 生成器(见第 12 章)。
6. CosIdGenerator 详解
6.1 什么是 CosIdGenerator?
CosIdGenerator 是 CosId 提供的一种完全自治的分布式 ID 生成算法,不依赖任何第三方中间件。
核心特点:
- 单机 TPS 性能:15,570,085 ops/s (是 UUID.randomUUID() 的 3 倍)
- 基于时钟的全局趋势递增 ID
- 可同时支持 100 万个实例
- 使用 Base36 或 Base62 编码压缩存储空间
6.2 位分配结构
Base36 格式(默认)
[timestamp(44-bit)]-[machineId(20-bit)]-[sequence(16-bit)] = 80 BITS = 17 CHARS
= [timestamp(8字符)]-[machineId(4字符)]-[sequence(3字符)]
Base62 格式
[timestamp(44-bit)]-[machineId(20-bit)]-[sequence(16-bit)] = 80 BITS = 15 CHARS
= [timestamp(9字符)]-[machineId(4字符)]-[sequence(4字符)]
6.3 配置示例
yaml
cosid:
cosid-generator:
# Base36 或 Base62
radix: base36
machine-bit: 20
sequence-bit: 16
timestamp-bit: 44
6.4 适用场景
✅ 适合使用 CosIdGenerator 的场景:
- 大规模集群部署(数千至数百万实例)
- 无法依赖外部中间件的轻量级服务
- 需要短 ID 字符串的场景(17 位 Base36 或 15 位 Base62)
❌ 不适合的场景:
- 需要严格单调递增的业务(订单号等)
- 需要纯数字 Long 类型 ID 的场景
7. SnowflakeId 雪花算法详解
7.1 算法原理
SnowflakeId 是 Twitter 开发的分布式唯一 ID 生成算法,使用 Long(64-bit)位分区来生成 ID。
标准位分配方案:
┌─────────────────────────────────────────────────────────────────┐
│ 64-bit SnowflakeId 结构 │
├────────────┬────────────────┬──────────────┬────────────────────┤
│ 1 bit 符号 │ timestamp │ machineId │ sequence │
│ (保留不用) │ (41-bit) │ (10-bit) │ (12-bit) │
├────────────┼────────────────┼──────────────┼────────────────────┤
│ 0 │ 41位时间戳 │ 10位机器号 │ 12位序列号 │
└────────────┴────────────────┴──────────────┴────────────────────┘
总位数: 1 + 41 + 10 + 12 = 64 bits (实际使用 63 位)
7.2 各字段含义与容量
| 字段 | 位数 | 容量 | 说明 |
|---|---|---|---|
| timestamp | 41-bit | 约 69 年 | (1L<<41)/(1000*3600*24*365) ≈ 69年,从自定义 EPOCH 开始计算 |
| machineId | 10-bit | 1024 个实例 | 相同业务可部署的副本数(Kubernetes Pod 数) |
| sequence | 12-bit | 4096/ms | 单机每毫秒可生成的 ID 数量 |
理论最大 TPS :4096000 * 1024 = 4,194,304,000 ≈ 41.9 亿/秒
7.3 设计优缺点分析
✅ 优点
- 本地单调递增:时间戳在高位,单实例保证时钟向前 → 本地单调递增
- 全局趋势递增:受全局时钟同步影响,整体呈趋势递增
- 无强依赖:不对任何第三方中间件有强依赖关系
- 高性能 :TPS 达到 4,096,000 ops/s(JMH 基准测试)
- 灵活配置:位分配方案可根据业务需要调整
❌ 缺点与挑战
- 时钟依赖:强依赖本地时钟,时钟回拨会导致 ID 重复
- 机器号维护:machineId 需要手动设置,大规模集群维护繁琐
- JavaScript 溢出:63-bit ID 超过 JS 的 Number.MAX_SAFE_INTEGER(53-bit)
7.4 CosId 对 SnowflakeId 的增强
┌─────────────────────────────────────────────────────────────┐
│ SnowflakeId 三大问题 & CosId 解决方案 │
├──────────────────┬──────────────────────────────────────────┤
│ ① 机器号分配问题 │ → MachineIdDistributor(6种分配策略) │
│ ② 时钟回拨问题 │ → ClockBackwardsSynchronizer(主动同步) │
│③ JS数值溢出问题 │ → SafeJavaScriptSnowflakeId(53-bit适配) │
└──────────────────┴──────────────────────────────────────────┘
7.5 两种时间精度实现
MillisecondSnowflakeId(默认)
java
// 使用 System.currentTimeMillis() 作为时间戳
// 精确到毫秒级别
// 单机 TPS: 409.6 万/秒(每毫秒 4096 个 ID)
SnowflakeId snowflakeId = new MillisecondSnowflakeId(...);
SecondSnowflakeId
java
// 使用 System.currentTimeMillis() / 1000 作为时间戳
// 精确到秒级别
// 单机 TPS: 409.6 万/秒(每秒 4096 个 ID),但序列号空间更大
SnowflakeId snowflakeId = new SecondSnowflakeId(...);
7.6 SnowflakeFriendlyId(友好型 ID)
将 SnowflakeId 解析为人类可读的格式:
java
public class SnowflakeIdState {
private final long id; // 原始 ID
private final int machineId; // 机器号
private final long sequence; // 序列号
private final LocalDateTime timestamp; // 生成时间
/**
* 友好格式: {timestamp}-{machineId}-{sequence}
* 示例: 20210623131730192-1-0
*/
private final String friendlyId;
}
// 使用方式
SnowflakeFriendlyId friendlyId = new DefaultSnowflakeFriendlyId(snowflakeId);
SnowflakeIdState state = friendlyId.friendlyId();
state.getFriendlyId(); // 输出: "20210623131730192-1-0"
7.7 SafeJavaScriptSnowflakeId(JS 安全型)
解决 JavaScript 的 Number.MAX_SAFE_INTEGER(53-bit)溢出问题:
java
// 自定义位分配,使总位数 ≤ 53-bit
// 示例: machineId=3bit, sequence=9bit → 总共 53-bit
SnowflakeId safeJsSnowflakeId = SafeJavaScriptSnowflakeId.ofMillisecond(1);
long id = safeJsSnowflakeId.generate();
// 返回给前端 JavaScript 不会溢出
7.8 ClockSyncSnowflakeId(时钟同步包装器)
java
// 当发生时钟回拨时,主动等待时钟同步后再生成 ID
// 而不是直接抛出异常
ClockSyncSnowflakeId clockSyncId = new ClockSyncSnowflakeId(
snowflakeId,
new DefaultClockBackwardsSynchronizer(spinThreshold, brokenThreshold)
);
配置开启:
yaml
cosid:
snowflake:
share:
clock-sync: true # 开启时钟同步模式
7.9 SnowflakeId 完整配置
yaml
cosid:
namespace: ${spring.application.name}
machine:
enabled: true
distributor:
type: redis # 可选: manual | stateful_set | redis | jdbc | zookeeper | mongo
guarder:
enabled: true # 开启机器号守护(定期续约)
snowflake:
enabled: true
zone-id: Asia/Shanghai # 时区
epoch: 1577203200000 # 自定义纪元时间戳(毫秒)
clock-backwards:
spin-threshold: 10 # 自旋等待阈值(毫秒)
broken-threshold: 2000 # 时钟回拨异常阈值(毫秒)
share:
clock-sync: true # 开启始终钟同步
friendly: true # 开启友好 ID 解析
provider:
# 注册多个命名 ID 生成器
order:
sequence-bit: 12
user:
sequence-bit: 12
safeJs:
machine-bit: 3 # JS 安全模式:缩短位数
sequence-bit: 9
8. SegmentId 号段模式详解
8.1 算法原理
**SegmentId(号段模式)**的核心思想:每次获取一段(Step)长度的可用 ID,减少网络 IO 请求频次,提升性能。
┌─────────────────────────────────────────────────────────────┐
│ SegmentId 工作流程 │
│ │
│ Instance 1 ──→ [请求号段] ──→ IdSegmentDistributor │
│ ↓ │
│ 获得 ID 区间: [1 ~ 1000] │
│ ↓ │
│ ┌─────────────────────────────────┐ │
│ │ 本地内存号段: 1,2,3,...,1000 │ ← 从内存取 ID,无网络IO │
│ └─────────────────────────────────┘ │
│ ↓ │
│ 号段用完后再次请求 │
│ │
│ Instance 2 ──→ [请求号段] ──→ IdSegmentDistributor │
│ ↓ │
│ 获得 ID 区间: [1001 ~ 2000] │
└─────────────────────────────────────────────────────────────┘
8.2 有序性特征
| 特征 | 说明 |
|---|---|
| 单实例 | 本地单调递增(每次获取的号段一定比上一次大) |
| 全局 | 趋势递增(受 Step 长度和集群规模影响) |
| 乱序程度 | Step 越小越有序;Step=1 时无限接近单调递增 |
8.3 IdSegmentDistributor 接口
java
/**
* 号段分发器核心接口
* 职责:在分布式实例之间分配连续的 ID 块
*/
public interface IdSegmentDistributor {
/**
* 获取下一个号段
* @param namespace 命名空间
* @param name ID 名称
* @return IdSegment 号段对象(包含当前最大 ID)
*/
IdSegment distribute(String namespace, String name);
/**
* 获取分组号段(支持按分组键分区)
*/
IdSegment distributeGrouped(String namespace, String name, String group);
}
8.4 四种号段分发器实现
| 分发器类型 | 类名 | 适用场景 | 特点 |
|---|---|---|---|
| Redis | RedisIdSegmentDistributor |
已有 Redis 基础设施 | 高性能、低延迟 |
| JDBC | JdbcIdSegmentDistributor |
关系型数据库为主 | 通用性强、事务安全 |
| ZooKeeper | ZookeeperIdSegmentDistributor |
已有 ZK 集群 | 强一致性保证 |
| MongoDB | MongoIdSegmentDistributor |
MongoDB 生态 | 文档型存储 |
8.5 JDBC 分发器初始化
使用 JDBC 分发器前需要创建表:
sql
CREATE TABLE IF NOT EXISTS cosid (
name VARCHAR(100) NOT NULL COMMENT '{namespace}.{name}',
last_max_id BIGINT NOT NULL DEFAULT 0,
last_fetch_time BIGINT NOT NULL,
CONSTRAINT cosid_pk PRIMARY KEY (name)
) ENGINE = InnoDB;
自动初始化配置(推荐):
yaml
cosid:
segment:
distributor:
type: jdbc
jdbc:
enable-auto-init-cosid-table: false # 是否自动建表
enable-auto-init-id-segment: true # 是否自动初始化号段记录
8.6 GroupedIdSegmentDistributor(分组分发器)
扩展了基础分发器,支持按分组键进行 ID 分区:
java
// 场景:租户隔离的 ID 生成
String tenantId = "tenant_001";
IdSegment segment = groupedDistributor.distributeGrouped(
"namespace",
"order_id",
tenantId
);
优势:
- 不同租户/分组的 ID 序列相互隔离
- 支持
allowReset()在分组变更时重置号段
8.7 SegmentId 配置示例
yaml
cosid:
segment:
enabled: true
mode: normal # 普通 SegmentId 模式(非链式)
distributor:
type: redis # redis | jdbc | zookeeper | mongo
share:
offset: 0 # 起始偏移量
step: 100 # 每次获取的号段长度
provider:
order:
offset: 10000 # 订单业务的起始偏移
step: 100
user:
offset: 20000 # 用户业务的起始偏移
step: 100
8.8 SegmentId 性能特征
| 指标 | 值 | 说明 |
|---|---|---|
| TPS | 29,506,073 ops/s | 号段未耗尽时的高性能 |
| P9999 延迟 | 46.624 us/op | 号段耗尽时同步获取导致延迟尖峰 |
| 稳定性 | 中等 | 受网络 IO 影响 |
9. SegmentChainId 号段链模式(推荐)⭐
9.1 为什么推荐 SegmentChainId?
SegmentChainId 是 SegmentId 的增强版,解决了 SegmentId 的稳定性问题:
┌─────────────────────────────────────────────────────────────┐
│ SegmentId vs SegmentChainId 对比 │
├─────────────────────┬───────────────────────────────────────┤
│ SegmentId │ SegmentChainId ⭐ │
├─────────────────────┼───────────────────────────────────────┤
│ 号段用完时同步等待 │ PrefetchWorker 预取,保持安全距离 │
│ P9999=46.624 us/op │ P9999=0.208 us/op(提升 224 倍!) │
│ TPS=2950万/s │ TPS=1.27亿+/s(提升 4.3 倍!) │
│ Step 固定不可变 │ 安全距离动态伸缩(饥饿膨胀/收缩) │
│ 无自适应性 │ 根据流量自动调整吞吐量 │
└─────────────────────┴───────────────────────────────────────┘
9.2 核心设计:PrefetchWorker(预取工作线程)
┌─────────────────────────────────────────────────────────────┐
│ SegmentChainId 架构设计 │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 号段链 (Segment Chain) │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 当前号段 │→ │ 缓冲号段1 │→ │ 缓冲号段2 │→ │ ... │ │ │
│ │ │(正在使用) │ │(预取备用) │ │(预取备用) │ │(预取中) │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ ↑ │ │
│ │ │ ID 获取线程(几乎无阻塞) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ PrefetchWorker (后台预取线程) │ │ │
│ │ │ 职责: 维护安全距离 (safeDistance) │ │ │
│ │ │ 策略: 饥饿检测 → 动态膨胀/收缩安全距离 │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ ↓ │ │
│ │ 向 IdSegmentDistributor 请求新号段 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ 安全距离 (safeDistance): │
│ ├─ 充足时: 收缩,减少内存占用和 ID 乱序 │
│ └─ 不足时 (饥饿): 膨胀,提高吞吐量 │
└─────────────────────────────────────────────────────────────┘
9.3 自适应机制(Adaptability)
SegmentChainId 引入饥饿状态概念,PrefetchWorker 根据饥饿状态动态调整安全距离:
流量突增场景:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━► 时间
↑流量 ↑流量下降
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ 饥饿状态 │ → 安全距离膨胀 │ 恢复正常 │ → 安全距离收缩
│ (step×2)│ │ │
└─────────┘ └─────────┘
膨胀系数 (Expansion): 默认为 2
公式: 新安全距离 = 当前安全距离 × Expansion
9.4 性能极限公式
TPS(SegmentChainId)极限值 = min(
TPS(AtomicLong), // 上限: AtomicLong 性能
(Step × Expansion) × TPS(Distributor) × T / s
)
其中:
- Step: 号段步长
- Expansion: 饥饿膨胀系数(默认 2)
- TPS(Distributor): 分发器的 TPS 性能
- T: 基准测试运行时长
- s: 秒
关键结论:
- 只要给足够的时间膨胀,任何分发器都能达到接近 AtomicLong 的性能上限
- Redis 分发器膨胀更快,达到上限所需时间更短
- 这解释了为什么不同性能的分发器最终都能达到 TPS=1.27亿/s
9.5 SegmentChainId 配置
yaml
cosid:
segment:
enabled: true
mode: chain # ⭐ 号段链模式(推荐)
chain:
safe-distance: 5 # 初始安全距离(预取号段数)
prefetch-worker:
core-pool-size: 2 # 预取线程池核心大小
prefetch-period: 1s # 预取周期
distributor:
type: redis # redis | jdbc | zookeeper | mongo
share:
offset: 0
step: 100 # 推荐:较小 Step,让 PrefetchWorker 自动调节
provider:
order:
offset: 10000
step: 100
payment:
offset: 20000
step: 50 # 高频业务可以用较小 Step
9.6 代码中使用 SegmentChainId
java
@Resource
private IdGeneratorProvider provider;
@Resource
private IdSegmentDistributorFactory idSegmentDistributorFactory;
/**
* 通过代码动态注册 SegmentChainId
*/
public void registerIdGenerator(String idName) {
IdSegmentDistributorDefinition definition =
new IdSegmentDistributorDefinition("namespace", idName, 0, 100);
IdSegmentDistributor distributor =
idSegmentDistributorFactory.create(definition);
SegmentChainId segmentChainId = new SegmentChainId(distributor);
provider.set(idName, segmentChainId);
}
/**
* 使用已注册的 ID 生成器
*/
public Long nextOrderId() {
return provider.get("order").generate();
}
10. 机器号分配器 MachineIdDistributor
10.1 概述
MachineIdDistributor 负责 SnowflakeId 的机器号分配,同时存储 MachineId 的上一次时间戳,用于启动时时钟回拨检查。
10.2 六种分配器实现
| # | 分配器类型 | 类名 | 适用场景 | 配置值 |
|---|---|---|---|---|
| 1 | 手动分配 | ManualMachineIdDistributor |
极小规模集群(< 5节点)、测试环境 | manual |
| 2 | K8s StatefulSet | StatefulSetMachineIdDistributor |
Kubernetes StatefulSet 部署 | stateful_set |
| 3 | Redis | RedisMachineIdDistributor |
已有 Redis 基础设施 | redis |
| 4 | JDBC | JdbcMachineIdDistributor |
关系型数据库为主 | jdbc |
| 5 | ZooKeeper | ZookeeperMachineIdDistributor |
已有 ZK 集群、强一致性要求 | zookeeper |
| 6 | MongoDB | MongoMachineIdDistributor |
MongoDB 生态 | mongo |
10.3 各分配器详细配置
① ManualMachineIdDistributor(手动)
yaml
cosid:
snowflake:
machine:
distributor:
type: manual
manual:
machine-id: 0 # 手动指定机器号(0~1023)
适用场景 :开发环境、极小规模生产集群
缺点:需要人工管理,容易重复或遗漏
② StatefulSetMachineIdDistributor(K8s StatefulSet)
yaml
cosid:
snowflake:
machine:
distributor:
type: stateful_set
# 自动从 Kubernetes StatefulSet 的 HOSTNAME 解析机器号
# 例如: order-service-0 → machineId=0
# order-service-1 → machineId=1
适用场景 :Kubernetes 生产环境、云原生部署
前提条件:Pod 必须以 StatefulSet 方式部署(而非 Deployment)
③ RedisMachineIdDistributor
yaml
cosid:
snowflake:
machine:
distributor:
type: redis
# 使用 Redis 存储机器号和最后时间戳
# 自动分配,无需手动配置
优点:
- 自动分配,无需人工干预
- 存储 MachineState(机器号 + 最后时间戳),支持启动时时钟回拨检查
- 利用已有的 Redis 基础设施
④ JdbcMachineIdDistributor
yaml
cosid:
snowflake:
machine:
distributor:
type: jdbc
jdbc:
enable-auto-init-cosid-machine-table: true # 自动建表
需要的表结构:
sql
CREATE TABLE IF NOT EXISTS cosid_machine (
namespace VARCHAR(100) NOT NULL,
instance_id VARCHAR(100) NOT NULL,
machine_id INT NOT NULL,
last_timestamp BIGINT NOT NULL,
PRIMARY KEY (namespace, instance_id),
INDEX idx_namespace (namespace),
INDEX idx_instance_id (instance_id)
) ENGINE = InnoDB;
⑤ ZookeeperMachineIdDistributor
yaml
cosid:
snowflake:
machine:
distributor:
type: zookeeper
# 使用 ZooKeeper 的临时顺序节点自动分配机器号
优点:强一致性保证、天然支持故障检测
⑥ MongoMachineIdDistributor
yaml
cosid:
snowflake:
machine:
distributor:
type: mongo
# 使用 MongoDB 存储机器号信息
10.4 MachineState(机器状态)
java
public class MachineState {
public static final MachineState NOT_FOUND = of(-1, -1);
private final int machineId; // 机器号
private final long lastTimeStamp; // 最后一次生成 ID 的时间戳
// getters, factory method of(...)
}
用途:
- 运行时时钟回拨检测:比较当前时间与
lastTimeStamp - 启动时时钟回拨检测:从外部存储恢复
lastTimeStamp
10.5 MachineStateStorage(机器状态存储)
yaml
# 本地文件存储(适用于稳定部署环境)
cosid:
snowflake:
machine:
state-storage:
local:
state-location: ./cosid-machine-state/
LocalMachineStateStorage:
- 使用本地文件存储 MachineState
- 仅适用于实例部署环境稳定的场景
- 如果 Pod 可能漂移到不同节点,应使用 Redis/JDBC 等分布式存储
10.6 机器号守护(Machine Guarder)
yaml
cosid:
snowflake:
machine:
guarder:
enabled: true # 开启守护线程,定期续约机器号
作用:防止因网络抖动等原因导致机器号被回收
11. 时钟回拨问题解决方案
11.1 问题背景
时钟回拨的致命问题是会导致 ID 重复、冲突,这是绝对不能容忍的。
重要理解 :我们解决的时钟回拨问题是当前 MachineId 的时钟回拨,而不是所有集群节点的时钟回拨。
11.2 两种时钟回拨场景
┌─────────────────────────────────────────────────────────────┐
│ 时钟回拨两种场景 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 场景1: 运行时时钟回拨 │
│ ───────────────────── │
│ 时间轴: ──────T1───────T2(T1+100ms)───T'(T1-50ms)─── │
│ ↑ ↑ │
│ 正常生成 时钟回拨! │
│ │
│ 解决: 存储 lastTimestamp,检测到回抛异常或等待同步 │
│ │
│ 场景2: 启动时时钟回拨 │
│ ───────────────────── │
│ 上次关闭: T_close = 2024-01-01 12:00:00 │
│ 本次启动: T_start = 2024-01-01 11:59:58 (时钟回拨2秒) │
│ │
│ 解决: 从外部存储恢复 MachineState,比较 lastTimestamp │
└─────────────────────────────────────────────────────────────┘
11.3 ClockBackwardsSynchronizer(时钟回拨同步器)
java
/**
* 默认的时钟回拨同步器
* 策略: 主动等待时钟同步
*/
public class DefaultClockBackwardsSynchronizer implements ClockBackwardsSynchronizer {
private final long spinThreshold; // 自旋阈值(默认 10ms)
private final long brokenThreshold; // 异常阈值(默认 2000ms)
/**
* 同步策略:
* 1. 回拨 < spinThreshold: 自旋等待(忙等待,CPU 密集)
* 2. 回拨 >= spinThreshold: 线程睡眠等待
* 3. 回拨 > brokenThreshold: 抛出 ClockTooManyBackwardsException
*/
public void synchronize(long backwardsMillis) { ... }
}
配置参数:
yaml
cosid:
snowflake:
clock-backwards:
spin-threshold: 10 # 自旋等待阈值(毫秒)
broken-threshold: 2000 # 异常阈值(毫秒,超过此值直接抛异常)
11.4 ClockSyncSnowflakeId vs 默认 SnowflakeId
| 特性 | 默认 SnowflakeId | ClockSyncSnowflakeId |
|---|---|---|
| 时钟回拨处理 | 直接抛出 ClockBackwardsException |
主动等待同步后重新生成 |
| 用户体验 | 差(下游无法处理) | 好(透明处理) |
| 推荐程度 | 不推荐 | 推荐生产使用 |
开启方式:
yaml
cosid:
snowflake:
share:
clock-sync: true # 开启时钟同步模式
12. IdGeneratorProvider 多业务支持
12.1 为什么需要 Provider?
在实际业务中,不同的业务通常使用不同的 ID 生成器:
- 订单服务:order-service → 订单 ID
- 用户服务:user-service → 用户 ID
- 支付服务:payment-service → 支付流水号
IdGeneratorProvider 就是 IdGenerator 的容器,通过业务名称获取对应的 IdGenerator。
12.2 通过配置文件注册
yaml
cosid:
segment:
enabled: true
mode: chain
distributor:
type: redis
share:
offset: 0
step: 100
provider: # ⭐ 注册多个命名 ID 生成器
order: # 名称为 "order" 的 ID 生成器
offset: 10000
step: 100
user: # 名称为 "user" 的 ID 生成器
offset: 20000
step: 100
payment: # 名称为 "payment" 的 ID 生成器
offset: 30000
step: 50
12.3 在代码中使用
java
@Service
public class OrderService {
@Resource
private IdGeneratorProvider provider;
/**
* 获取指定业务的 ID 生成器
*/
public Long createOrder() {
// 获取名为 "order" 的 ID 生成器
IdGenerator orderGenerator = provider.get("order");
Long orderId = orderGenerator.generate();
// ... 创建订单逻辑
return orderId;
}
public Long createUser() {
IdGenerator userGenerator = provider.get("user");
return userGenerator.generate();
}
}
12.4 通过代码动态注册
java
@Component
public class IdGeneratorInitializer implements CommandLineRunner {
@Resource
private IdGeneratorProvider provider;
@Resource
private IdSegmentDistributorFactory idSegmentDistributorFactory;
@Override
public void run(String... args) throws Exception {
// 动态注册新的 ID 生成器
registerIdGenerator("invoice");
registerIdGenerator("refund");
}
private void registerIdGenerator(String idName) {
IdSegmentDistributorDefinition definition =
new IdSegmentDistributorDefinition("my_app", idName, 0, 100);
IdSegmentDistributor distributor =
idSegmentDistributorFactory.create(definition);
SegmentChainId segmentChainId = new SegmentChainId(distributor);
provider.set(idName, segmentChainId);
}
}
12.5 Bean Name 规则
| 配置名称 | Bean Name | 类型 |
|---|---|---|
| 共享(默认) | __share__SegmentId |
SegmentId |
| 共享(默认) | __share__SnowflakeId |
SnowflakeId |
命名: order |
orderSegmentId |
SegmentId |
命名: user |
userSnowflakeId |
SnowflakeId |
注入方式:
java
@Autowired
@Qualifier("__share__SegmentId")
@Lazy
private SegmentId segmentId;
@Autowired
@Qualifier("orderSegmentId")
@Lazy
private SegmentId orderSegmentId;
13. MyBatis 插件集成
13.1 概述
CosId 提供 MyBatis 插件 (CosIdPlugin),通过 @CosId 注解实现实体类字段的自动 ID 生成。
13.2 添加依赖
kotlin
// Gradle Kotlin DSL
implementation("me.ahoo.cosid:cosid-mybatis:${cosidVersion}")
xml
<!-- Maven -->
<dependency>
<groupId>me.ahoo.cosid</groupId>
<artifactId>cosid-mybatis</artifactId>
<version>${cosid.version}</version>
</dependency>
13.3 @CosId 注解说明
java
@Target({ElementType.FIELD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CosId {
/**
* ID 生成器名称,默认使用共享生成器 (__share__)
*/
String value() default IdGeneratorProvider.SHARE;
/**
* 是否使用友好 ID 格式(SnowflakeIdState.toString())
* 如: 20210623131730192-1-0
*/
boolean friendlyId() default false;
}
13.4 使用示例
场景一:Long 类型 ID
java
public class OrderEntity {
@CosId(value = "order") // 使用名为 "order" 的 ID 生成器
private Long id;
private String orderNo;
private BigDecimal amount;
// getters and setters...
}
场景二:友好 ID(String 类型)
java
public class FriendlyIdEntity {
@CosId(friendlyId = true) // 生成友好格式的字符串 ID
private String id;
// id 值示例: "20210623131730192-1-0"
// getters and setters...
}
场景三:Mapper 接口
java
@Mapper
public interface OrderRepository {
@Insert("INSERT INTO t_order (id, order_no, amount) VALUES (#{id}, #{orderNo}, #{amount})")
void insert(OrderEntity order);
@Insert({
"<script>",
"INSERT INTO t_friendly_table (id)",
"VALUES",
"<foreach item='item' collection='list' open='' separator=',' close=''>",
"(#{item.id})",
"</foreach>",
"</script>"
})
void insertList(List<FriendlyIdEntity> list);
}
13.5 使用效果
java
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public OrderEntity createOrder() {
OrderEntity order = new OrderEntity();
order.setOrderNo("ORD-20240101-001");
order.setAmount(new BigDecimal("99.99"));
// 插入时,@CosId 注解的字段会自动填充 ID
orderRepository.insert(order);
// order.getId() 已经有值,例如: 208796080181248
return order;
/**
* 返回结果:
* {
* "id": 208796080181248,
* "orderNo": "ORD-20240101-001",
* "amount": 99.99
* }
*/
}
}
14. ShardingSphere 分片集成
14.1 概述
CosId 为 Apache ShardingSphere 提供了两个高性能插件:
- CosIdKeyGenerateAlgorithm - 分布式主键生成
- CosIdIntervalShardingAlgorithm - 基于间隔的时间范围分片
- CosIdModShardingAlgorithm - 高性能取模分片
14.2 CosIdKeyGenerateAlgorithm(分布式主键)
yaml
spring:
shardingsphere:
rules:
sharding:
key-generators:
cosid:
type: COSID # 使用 CosId 作为主键生成器
props:
id-name: __share__ # 指定使用的 ID 生成器名称
14.3 CosIdIntervalShardingAlgorithm(时间范围分片)
优势对比官方实现:
| 特性 | CosId 实现 | 官方 IntervalShardingAlgorithm |
|---|---|---|
| 数据类型支持 | Long/LocalDateTime/DATE/String/SnowflakeId | 先转 String 再转 LocalDateTime |
| 转换成功率 | 高(直接解析) | 受时间格式化字符影响 |
| 性能 | 高出 1200~4000 倍 | 基准 |
配置示例:
yaml
spring:
shardingsphere:
rules:
sharding:
sharding-algorithms:
order-date-interval:
type: COSID_INTERVAL # CosId 时间范围分片
props:
logic-name-prefix: t_order_ # 逻辑表名前缀
id-name: cosid-name # ID 生成器名称
datetime-lower: 2021-12-08 22:00:00 # 时间下界
datetime-upper: 2022-12-01 00:00:00 # 时间上界
sharding-suffix-pattern: yyyyMM # 分片后缀格式
datetime-interval-unit: MONTHS # 间隔单位
datetime-interval-amount: 1 # 间隔数量
tables:
t_order_log:
actual-data-nodes: ds0.t_order_log_{202112..202212}
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: order-date-interval
支持的类型变体:
| Type | 说明 |
|---|---|
COSID_INTERVAL |
通用时间范围分片 |
COSID_INTERVAL_SNOWFLAKE |
SnowflakeId 时间范围分片(直接从 ID 解析时间) |
14.4 CosIdModShardingAlgorithm(取模分片)
性能优势 :比官方 ModShardingAlgorithm 高出 1200~4000 倍,且不会出现严重性能退化
yaml
spring:
shardingsphere:
rules:
sharding:
sharding-algorithms:
order-mod:
type: COSID_MOD # CosId 取模分片
props:
mod: 4 # 取模数(分片数量)
logic-name-prefix: t_order_ # 逻辑表名前缀
tables:
t_order:
actual-data-nodes: ds0.t_order_{0..3}
table-strategy:
standard:
sharding-column: id
sharding-algorithm-name: order-mod
15. 完整配置参考手册
15.1 完整 application.yml 模板
yaml
# ============================================================
# CosId 完整配置模板(生产环境参考)
# ============================================================
spring:
data:
redis:
host: localhost
port: 6379
datasource:
url: jdbc:mysql://localhost:3306/cosid_db?useUnicode=true&characterEncoding=utf-8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
cosid:
# ----------------------------------------------------------
# 基础配置
# ----------------------------------------------------------
namespace: ${spring.application.name} # 命名空间,用于隔离不同应用
# ----------------------------------------------------------
# 机器号分配(SnowflakeId 需要)
# ----------------------------------------------------------
machine:
enabled: true
# stable: true
# machine-bit: 10
# instance-id: ${HOSTNAME}
distributor:
type: redis # manual | stateful_set | redis | jdbc | zookeeper | mongo
# manual:
# machine-id: 0
guarder:
enabled: true # 开启机器号守护
state-storage:
local:
state-location: ./cosid-machine-state/
# ----------------------------------------------------------
# SnowflakeId 雪花算法配置
# ----------------------------------------------------------
snowflake:
enabled: true
# epoch: 1577203200000 # 自定义纪元(毫秒时间戳)
zone-id: Asia/Shanghai
clock-backwards:
spin-threshold: 10 # 自旋等待阈值(毫秒)
broken-threshold: 2000 # 异常阈值(毫秒)
share:
clock-sync: true # 开启时钟同步模式(推荐)
friendly: true # 开启友好 ID 解析
provider:
# 注册多个命名 SnowflakeId 生成器
bizA:
sequence-bit: 12
bizB:
sequence-bit: 12
safeJs:
machine-bit: 3 # JS 安全模式
sequence-bit: 9
# ----------------------------------------------------------
# SegmentId / SegmentChainId 号段算法配置
# ----------------------------------------------------------
segment:
enabled: true
mode: chain # normal | chain(推荐 chain)
chain:
safe-distance: 5 # 初始安全距离
prefetch-worker:
core-pool-size: 2 # 预取线程池大小
prefetch-period: 1s # 预取周期
distributor:
type: redis # redis | jdbc | zookeeper | mongo
jdbc:
enable-auto-init-cosid-table: false
enable-auto-init-id-segment: true
share:
offset: 0 # 全局起始偏移量
step: 100 # 全局默认步长
provider:
# 注册多个命名 SegmentChainId 生成器
order:
offset: 10000
step: 100
user:
offset: 20000
step: 100
payment:
offset: 30000
step: 50
# ----------------------------------------------------------
# CosIdGenerator 配置(可选)
# ----------------------------------------------------------
# cosid-generator:
# radix: base36 # base36 | base62
15.2 配置项速查表
| 配置路径 | 类型 | 默认值 | 说明 |
|---|---|---|---|
cosid.namespace |
String | ${spring.application.name} |
命名空间 |
cosid.machine.enabled |
Boolean | false | 是否启用机器号分配 |
cosid.machine.distributor.type |
String | - | 机器号分发器类型 |
cosid.snowflake.enabled |
Boolean | false | 是否启用 SnowflakeId |
cosid.snowflake.epoch |
Long | - | 自定义纪元时间戳 |
cosid.snowflake.clock-backwards.spin-threshold |
Integer | 10 | 自旋阈值(ms) |
cosid.snowflake.clock-backwards.broken-threshold |
Integer | 2000 | 异常阈值(ms) |
cosid.snowflake.share.clock-sync |
Boolean | false | 时钟同步模式 |
cosid.snowflake.share.friendly |
Boolean | false | 友好 ID 模式 |
cosid.segment.enabled |
Boolean | false | 是否启用号段模式 |
cosid.segment.mode |
String | normal | normal / chain |
cosid.segment.chain.safe-distance |
Integer | 5 | 安全距离 |
cosid.segment.distributor.type |
String | - | 号段分发器类型 |
cosid.segment.share.offset |
Long | 0 | 起始偏移量 |
cosid.segment.share.step |
Integer | 100 | 默认步长 |
16. 性能基准测试
16.1 测试环境
| 项目 | 规格 |
|---|---|
| 开发机 | MacBook Pro (M1) |
| Java 版本 | Java 17 |
| Redis | 本机部署 |
| 测试工具 | JMH (Java Microbenchmark Harness) |
16.2 SnowflakeId 性能
Benchmark Mode Cnt Score Error Units
SnowflakeIdBenchmark.millisecondSnowflakeId_friendlyId thrpt 3 4020311.665 ops/s
SnowflakeIdBenchmark.millisecondSnowflakeId_generate thrpt 3 4095403.859 ops/s
SnowflakeIdBenchmark.safeJsMillisecondSnowflakeId_generate thrpt 3 511654.048 ops/s
SnowflakeIdBenchmark.safeJsSecondSnowflakeId_generate thrpt 3 539818.563 ops/s
SnowflakeIdBenchmark.secondSnowflakeId_generate thrpt 3 4206843.941 ops/s
解读:
- 标准 SnowflakeId: ~409 万 ops/s
- SafeJavaScriptSnowflakeId: ~51 万 ops/s(因为位数少,序列号空间小)
- FriendlyId(带解析): ~402 万 ops/s(略有开销)
16.3 SegmentChainId 性能
| 指标 | 值 | 说明 |
|---|---|---|
| TPS | 127,439,148+ ops/s | 接近 AtomicLong 性能上限 |
| P9999 延迟 | 0.208 us/op | 极其稳定 |
| 对比 UUID | 41x 更快 | UUID: 307万 ops/s |
| 对比 Leaf(segment) | 5x 更快 | 美团 Leaf segment 模式 |
16.4 各算法性能汇总
TPS 性能对比 (ops/s):
UUID/GUID: ████ 3,078,638
SnowflakeId: ████████████████████ 40,960,000
SegmentId: ████████████████████████████████████████ 29,506,073
SegmentChainId: █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ 127,439,148+
CosIdGenerator: ██████████████████████████████████████████████████████████ 15,570,085
16.5 如何运行基准测试
bash
# 方式一:Gradle 任务
gradle cosid-core:jmh
# 方式二:直接运行 JAR
java -jar cosid-core/build/libs/cosid-core-3.2.0-jmh.jar \
-bm thrpt \
-wi 1 \
-rf json \
-f 1
17. 最佳实践与生产建议
17.1 算法选择决策树
你需要分布式 ID?
│
├─ 需要纯数字 Long 类型?
│ │
│ ├─ 高吞吐(> 1000万 TPS)?
│ │ └─ ★★★ 使用 SegmentChainId(推荐)
│ │
│ ├─ 中等吞吐(100万~1000万 TPS)?
│ │ ├─ 已有 Redis/MongoDB → SegmentChainId
│ │ └─ 无外部依赖 → SnowflakeId
│ │
│ └─ 低吞吐(< 100万 TPS)?
│ └─ SnowflakeId(简单可靠)
│
├─ 可以接受字符串 ID?
│ │
│ ├─ 需要短 ID(< 20字符)?
│ │ └─ CosIdGenerator(Base36/Base62)
│ │
│ └─ 需要可读 ID?
│ └─ SnowflakeFriendlyId(格式: 时间-机器-序列)
│
└─ 需要前端 JavaScript 兼容?
└─ SafeJavaScriptSnowflakeId(≤ 53-bit)
17.2 生产环境 Checklist
✅ 必做项
- 选择合适的分发器:优先使用已有基础设施(Redis/JDBC)
- 开启时钟同步 :
cosid.snowflake.share.clock-sync: true - 配置机器号守护 :
cosid.machine.guarder.enabled: true - 设置合理的 epoch:自定义纪元为产品上线时间
- 监控 ID 生成速率:避免接近理论上限
- 备份 MachineState:确保重启后能恢复状态
⚠️ 注意事项
- 不要同时启用共享的 SnowflakeId 和 SegmentId(会冲突)
- Step 不要设置过大(建议 50~500,让 PrefetchWorker 自动调节)
- safeDistance 不要设置过小(建议 ≥ 3)
- JDBC 分发器注意连接池配置
🔧 高级优化
- 按业务拆分 provider:不同业务使用不同命名空间
- 监控 PrefetchWorker 状态:观察饥饿膨胀频率
- 设置 brokenThreshold 合理值:建议 1000~3000ms
- 考虑多机房容灾:分发器跨机房部署
17.3 分发器选择指南
| 场景 | 推荐分发器 | 理由 |
|---|---|---|
| 已有 Redis | Redis | 高性能、低延迟、已有连接 |
| 纯数据库架构 | JDBC | 无额外组件、事务一致 |
| 强一致性要求 | ZooKeeper | CP 模型、原生分布式锁 |
| MongoDB 生态 | MongoDB | 文档模型自然适配 |
| K8s 原生部署 | StatefulSet | 无需外部依赖 |
| 开发/测试 | Manual | 最简单、可控 |
17.4 监控指标建议
| 指标 | 说明 | 告警阈值 |
|---|---|---|
| ID 生成 TPS | 每秒生成 ID 数量 | 接近理论上限的 80% |
| 号段获取耗时 | 从分发器获取号段的延迟 | > 100ms |
| 时钟回拨次数 | 发生时钟回拨的频率 | > 10次/分钟 |
| PrefetchWorker 饥饿次数 | 安全距离不足的频率 | 持续饥饿 |
| 机器号分配失败 | 无法获取机器号的次数 | > 0 |
18. 常见问题 FAQ
Q1: CosId 和美团 Leaf 有什么区别?
| 维度 | CosId (SegmentChainId) | Leaf (segment) |
|---|---|---|
| 性能 | 5 倍 faster | 基准 |
| 架构 | 嵌入式 SDK | 独立服务(Leaf-Server) |
| 部署复杂度 | 低(SDK 模式) | 高(需部署 Leaf-Server) |
| 语言支持 | Java (Spring Boot) | Java (Spring Boot) |
| 算法丰富度 | 4 种算法 | 2 种算法 |
Q2: 同时开启 SnowflakeId 和 SegmentId 会怎样?
只有其中一个共享的 (__share__) IdGenerator 会注入到 Spring 容器,另一个会被忽略(Bean 名称冲突)。
解决方案 :通过 provider 配置显式注册多个命名 ID 生成器。
Q3: SegmentChainId 的安全距离应该设多大?
- 初始值 :推荐
5(即预取 5 个号段作为缓冲) - 运行时:PrefetchWorker 会根据饥饿状态自动调整
- 原则:宁可稍大(浪费少量 ID),不要过小(影响性能)
Q4: 为什么不同分发器最终都能达到相同性能?
因为 SegmentChainId 的极限性能主要取决于 AtomicLong 的性能上限 和 饥饿膨胀机制,而不是分发器本身的 TPS。只要给足够时间膨胀,任何分发器都能达到接近上限的性能。
区别在于:高性能分发器(如 Redis)达到上限所需时间更短。
Q5: 如何处理 JavaScript 的 Long 溢出问题?
三种方案:
- 转 String:直接将 Long 转 JSON 字符串
- SafeJavaScriptSnowflakeId:使用 ≤ 53-bit 的位分配
- SnowflakeFriendlyId:返回友好的字符串格式
Q6: 时钟回拨应该如何处理?
- 首选 :开启
clock-sync: true,让框架自动处理 - 监控:设置告警,及时发现时钟问题
- 根因:检查 NTP 配置、虚拟机时钟同步
Q7: CosId 支持哪些数据库?
- JDBC 分发器:支持所有 JDBC 兼容的关系型数据库(MySQL、PostgreSQL、Oracle、SQL Server 等)
- MongoDB 分发器:MongoDB 3.6+
Q8: 如何在现有项目中迁移到 CosId?
步骤:
- 添加 CosId 依赖
- 配置分发器(选择已有基础设施)
- 逐步替换现有 ID 生成逻辑
- 验证 ID 唯一性和有序性
- 监控性能指标