ID生成策略
ID生成策略
ID生成是系统设计中重要的一环,不同的场景需要不同的ID生成策略。
自增ID
自增ID是数据库中最简单的ID生成方式,通常通过数据库的自动递增功能实现(如MySQL的AUTO_INCREMENT)
优点
简单易用:数据库原生支持,无需额外开发
绝对有序:ID严格按插入顺序递增
空间效率高:通常使用32位或64位整数
索引效率高:B+树索引对连续数字非常友好
缺点
可预测性:容易暴露业务量,存在安全风险
分布式限制:单数据库有效,分库分表时难以保证全局唯一
依赖数据库:高并发时可能成为瓶颈
迁移困难:合并数据时可能发生冲突
适用场景
单机数据库应用
不需要隐藏业务规模的内部系统
对ID连续性有强需求的场景
例子
create table `user`
(id int primary key auto_increment,`name` char(20)
);
UUID
UUID(Universally Unique Identifier)是一个128位的全局唯一标识符,有多个版本,最常用的是版本4(随机生成)
优点
全局唯一:几乎不可能重复(理论上有但概率极低)
无需协调:任何节点可独立生成
信息安全:随机性强,无法预测
无中心依赖:不依赖数据库或任何服务
缺点
存储空间大:128位,字符串形式36字节
无序性:导致数据库索引效率低下
可读性差:对人不友好
传输开销:在网络传输中占用更多带宽
适用场景
分布式系统需要快速生成唯一ID
临时标识或一次性令牌
不关心排序且ID不频繁查询的场景
例子
Java标准库(java.util.UUID)提供了完整的UUID生成和解析功能,无需任何第三方依赖
import java.util.UUID;
public class Main {public static void main(String[] args) {UUID uuid = UUID.randomUUID();System.out.println(uuid);}
}
雪花算法(Snowflake)
优点
分布式友好:通过workerId支持分布式部署
趋势递增:利于数据库索引
时间有序:ID中包含时间信息
高性能:本地生成无网络开销
可控长度:64位长整型,存储高效
缺点
时钟依赖:系统时钟回拨会导致异常
workerId分配:需要额外系统管理workerId
时间溢出:41位时间戳约69年后会耗尽
不完全连续:同一毫秒内连续,跨毫秒不连续
适用场景
分布式系统需要有序唯一ID
需要ID中包含时间信息的场景
高性能ID生成需求
例子
MyBatis-Plus的IdWorker实现
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
public class Main {public static void main(String[] args) {long id = IdWorker.getId(); // 使用默认workerIdSystem.out.println(id);}
}
Redis生成
Redis作为高性能的内存数据库,非常适合用来生成分布式ID
优点
极其简单
保证原子性和唯一性
高性能(Redis单节点可达10万+/秒)
缺点
纯数字可预测
单点故障风险
无时间等额外信息
高并发场景
分段缓存方案
例子
(时间戳+计数)方案实现
@Component
public class RedisIdWorker
{@AutowiredRedisTemplate redisTemplate;//开始时间搓public static final long BEGIN_TIMESTAMP;static {LocalDateTime time = LocalDateTime.of(2025,1,1,0,0,0);BEGIN_TIMESTAMP = time.toEpochSecond(ZoneOffset.UTC);}private static final String INC_KEY = "inc:";private static final int COUNT_BITS = 32; public long nextId(String keyPrefix){//1.生成时间搓LocalDateTime now = LocalDateTime.now();long nowTime = now.toEpochSecond(ZoneOffset.UTC);long timeStamp = nowTime - BEGIN_TIMESTAMP;//2.生成序列号//获取当前日期,精确到天String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));//自增长long increment = redisTemplate.opsForValue().increment(INC_KEY + keyPrefix + ":" + date);//3.拼接并返回return timeStamp << 32 | increment;}
结果: