unique id

在微服务架构中,确保合同 ID 的唯一性可以通过以下几种方法实现:

  1. 使用分布式唯一 ID 生成器

使用分布式 ID 生成器是一种常见的方式,适合需要高效生成唯一 ID 的场景。以下是几种常见方案:

方案 1:雪花算法(Snowflake)

雪花算法生成 64 位的唯一 ID,包含时间戳、机器 ID 和序列号,适合分布式场景。
• 优点:高性能、生成的 ID 按时间递增。
• 实现:
• 使用现有库(例如,Twitter 的 Snowflake)。
• 自行实现,或利用开源组件,如 Baeldung Snowflake。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class SnowflakeIdGenerator {
private final long epoch = 1609459200000L; // 自定义开始时间戳
private final long workerIdBits = 5L;
private final long datacenterIdBits = 5L;
private final long sequenceBits = 12L;

private final long maxWorkerId = ~(-1L << workerIdBits);
private final long maxDatacenterId = ~(-1L << datacenterIdBits);

private final long workerIdShift = sequenceBits;
private final long datacenterIdShift = sequenceBits + workerIdBits;
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

private final long sequenceMask = ~(-1L << sequenceBits);

private long workerId;
private long datacenterId;
private long sequence = 0L;
private long lastTimestamp = -1L;

public SnowflakeIdGenerator(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("Worker ID out of range");
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException("Datacenter ID out of range");
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}

public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards");
}
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = nextTimestamp(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;

return ((timestamp - epoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift)
| sequence;
}

private long nextTimestamp(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
}

使用示例:

1
2
3
SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);
long contractId = generator.nextId();
System.out.println(contractId);

方案 2:UUID

利用 Java 的 UUID 生成唯一 ID,适合生成少量 ID 的场景。

示例代码:

1
2
3
4
5
6
7
import java.util.UUID;

public class ContractIdService {
public String generateUniqueId() {
return UUID.randomUUID().toString();
}
}

优缺点:
• 优点:无需依赖其他服务,简单易用。
• 缺点:生成的 ID 较长,可能不适合需要短 ID 的场景。

  1. 利用数据库的自增特性

如果有一个中心数据库,可以使用数据库的自增特性为每个合同生成唯一 ID。
• 在数据库中创建一个 contract_id_generator 表:

1
2
3
CREATE TABLE contract_id_generator (
id BIGINT AUTO_INCREMENT PRIMARY KEY
);
•	每次需要生成 ID 时,插入一条记录并获取 id:

// 插入记录并获取自增 ID

1
2
3
4
5
6
7
String sql = "INSERT INTO contract_id_generator VALUES ()";
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
long contractId = rs.getLong(1);
}

优缺点:
• 优点:简单直观,易于实现。
• 缺点:需要一个中心数据库,可能存在性能瓶颈。

  1. 利用 Redis 分布式计数器

Redis 提供分布式计数功能,可以高效生成递增的唯一 ID。
• 配置 Redis:确保微服务能够连接到同一个 Redis 实例。
• 生成合同 ID:

1
2
3
4
5
6
7
8
9
10
11
12
13
import redis.clients.jedis.Jedis;

public class RedisIdGenerator {
private Jedis jedis;

public RedisIdGenerator(String redisHost, int redisPort) {
this.jedis = new Jedis(redisHost, redisPort);
}

public long nextId(String key) {
return jedis.incr(key);
}
}

// 使用

1
2
3
RedisIdGenerator idGenerator = new RedisIdGenerator("localhost", 6379);
long contractId = idGenerator.nextId("contract_id");
System.out.println(contractId);

优缺点:
• 优点:高性能,分布式支持。
• 缺点:需要维护 Redis 服务。

  1. 将模板与合同组合

将 contract_id 与 template_id 组合:

1
2
3
public String generateContractReference(long contractId, long templateId) {
return "CT-" + templateId + "-" + contractId;
}

生成的合同引用:

“CT-1001-345678901234”

将此合同引用存储到合同表中,或者直接用作合同的主键。

推荐方案
1. 如果你的系统规模较大,推荐使用 Snowflake 或 Redis 分布式计数器。
2. 如果系统对 ID 格式有严格要求(如递增 ID),可以选择数据库自增主键。
3. 通过合同模板与 ID 组合生成最终合同引用,既可以在前端显示,也能作为后端索引数据的唯一标识。

这样既能保证合同 ID 的唯一性,又能将合同模板与合同实例绑定在一起,形成完整的合同数据链路。