unique id 在微服务架构中,确保合同 ID 的唯一性可以通过以下几种方法实现:
使用分布式唯一 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 的场景。
利用数据库的自增特性
如果有一个中心数据库,可以使用数据库的自增特性为每个合同生成唯一 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 ); }
优缺点: • 优点:简单直观,易于实现。 • 缺点:需要一个中心数据库,可能存在性能瓶颈。
利用 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 服务。
将模板与合同组合
将 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 的唯一性,又能将合同模板与合同实例绑定在一起,形成完整的合同数据链路。