最近在做项目,当你数据库表中的id要向用户展示时,或者在分布式系统中,如果只用数据库id自增作为唯一id的策略的话,会出现一系列问题,比如安全性问题、数据冲突问题(在分布式系统中)等,所以需要一种方法来生成全局唯一id并且可以避免这些问题。全局唯一 ID(GUID/UUID)是许多系统开发中需要解决的问题,尤其是在分布式系统中,必须保证生成的 ID 不重复且高效。下面我总结了一下常见的几种策略。
1. 基于 UUID
UUID(Universally Unique Identifier)是一个通用的唯一标识符,通常用于生成 128 位的唯一 ID。
特点
- 无需依赖中心化服务。
- 快速生成且唯一性高。
- 格式如:
550e8400-e29b-41d4-a716-446655440000
。
实现方式
import java.util.UUID;
public class UUIDExample {
public static void main(String[] args) {
// 生成一个 UUID
String uniqueID = UUID.randomUUID().toString();
System.out.println(uniqueID); // 输出类似于:550e8400-e29b-41d4-a716-446655440000
}
}
优缺点
- 优点:
- 无需中心化依赖,分布式系统中可独立生成。
- 实现简单,生成速度快。
- 缺点:
- UUID 较长(128 位),不适合用于高并发环境中的排序场景。
- 可读性差。
2. 基于数据库的自增 ID
通过数据库的自增字段实现唯一 ID。
特点
- 依赖数据库,自增字段能保证唯一性。
- ID 格式整齐(如:1、2、3、4……)。
实现方式
MySQL 示例:
创建一个表,利用自增主键:
CREATE TABLE unique_id (
id BIGINT AUTO_INCREMENT PRIMARY KEY
);
每次需要生成 ID 时,插入一条记录,获取自增 ID:
INSERT INTO unique_id () VALUES ();
SELECT LAST_INSERT_ID();
优缺点
- 优点:
- 实现简单,生成的 ID 连续且有序。
- 缺点:
- 数据库成为性能瓶颈,不适合高并发场景。
- 分布式场景下需要额外设计解决方案(如:分库分表的 ID 生成)。
3. 基于 Redis 的自增键
利用 Redis 的原子性自增操作生成全局唯一 ID。
特点
INCR
是 Redis 的原子操作,可以在分布式系统中安全使用。- Redis 性能高,适合高并发场景。
实现方式
import redis.clients.jedis.Jedis;
public class RedisIncrExample {
public static void main(String[] args) {
// 连接 Redis
Jedis jedis = new Jedis("localhost", 6379);
// 自增键
Long uniqueId = jedis.incr("global_id");
System.out.println(uniqueId); // 输出递增的整数
jedis.close();
}
}
优缺点
- 优点:
- 性能高,适合分布式系统。
- 生成的 ID 简单、递增,便于排序。
- 缺点:
- 需要依赖 Redis 服务。
4. 雪花算法(Snowflake)
雪花算法是 Twitter 提出的分布式唯一 ID 生成算法,生成 64 位的整型 ID,结构如下:
1 bit 符号位 | 41 bit 时间戳 | 10 bit 机器 ID | 12 bit 序列号
- 时间戳:表示从某个时间点到当前时间的毫秒数,41 位可用 69 年。
- 机器 ID:用于区分不同节点或机器,10 位支持最多 1024 个节点。
- 序列号:支持每毫秒生成最多 4096 个 ID。
实现方式
使用 Hutool 工具库:
import cn.hutool.core.util.IdUtil;
public class SnowflakeExample {
public static void main(String[] args) {
// 创建雪花算法生成器
long workerId = 1; // 机器 ID
long datacenterId = 1; // 数据中心 ID
cn.hutool.core.lang.Snowflake snowflake = IdUtil.createSnowflake(workerId, datacenterId);
// 生成唯一 ID
long uniqueId = snowflake.nextId();
System.out.println(uniqueId); // 输出类似:123456789012345678
}
}
优缺点
- 优点:
- 高性能,适合高并发场景。
- ID 是时间有序的,方便排序。
- 缺点:
- 实现复杂,需要管理机器 ID。
5. 基于时间戳与随机数
将时间戳和随机数结合生成唯一 ID。
特点
- 时间戳保证了唯一性,随机数降低了冲突概率。
- 简单易实现。
实现方式
import java.util.Random;
public class TimestampRandomExample {
public static void main(String[] args) {
long timestamp = System.currentTimeMillis(); // 当前时间戳
int randomNum = new Random().nextInt(9000) + 1000; // 4 位随机数
String uniqueId = timestamp + "" + randomNum;
System.out.println(uniqueId); // 输出类似:167223867512345
}
}
优缺点
- 优点:
- 简单易用,不依赖外部服务。
- 缺点:
- 不适合极高并发场景(可能会有冲突)。
6.总结
生成策略 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
UUID | 无需中心化、实现简单 | 长度较大,排序困难 | 普通系统中标识符生成 |
数据库自增 | 简单有序、连续 | 数据库性能瓶颈,分布式支持较差 | 小型系统或单点系统 |
Redis 自增 | 高性能、支持分布式 | 依赖 Redis 服务 | 分布式系统,高并发场景 |
雪花算法 | 高性能、时间有序、分布式支持好 | 实现复杂 | 高并发分布式系统 |
时间戳+随机数 | 简单易实现 | 冲突概率较高,排序困难 | 普通系统,低并发环境 |
以上是总结的一些方案,最近看了一下黑马点评生成全局 唯一id的策略,感觉不错,以后有机会单独写一期,over!