在服务器集群环境下避免生成重复唯一编号,需解决机器时间不一致、事务提交延迟等核心问题,可通过优化事务机制或引入分布式协调服务实现。以下是具体解决方案及分析:
一、问题分析:重复编号的根源机器时间不同步集群中各节点可能因NTP服务配置差异、时钟漂移或时区设置错误困袭,导致基于年月日生成的编号在不同机器上重复(如机器A处于2023-10-01 23:59:59,机器B已进入2023-10-02 00:00:00,若编号规则包含日期,可能生成相同编号)。
事务提交延迟即使使用Redis锁,若事务未及时提交,其他线程可能误判资源可用性,导致并发操作生成重复编号。例如:
线程A获取锁后生成编号,但事务未提交前锁释放;
线程B获取锁并生成相同编号(因依赖的共享数据未更新)。
二、解决方案:事务机制优化方法1:调整事务传播行为- 原理:将事务传播机制改为Propagation.REQUIRES_NEW,强制每次编号生成操作在新事务中独立执行,避免因外层事务未提交导致的数据不一致。
- 代码示例:@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)public String generateUniqueId() { // 生成编号逻辑(如结合日期、序列号等) return currentDate + "-" + sequenceService.getNextSequence();}
- 适用场景:编号生成逻辑简单,且对性能要求较高的场景。
- 优势:
确保事务立即提交,减少锁竞争;
避免脏数据风险。
- 局限:
频繁创建新事务可能增加数据库压力;
仍需解决机器时间同步问题。
方法2:手动控制事务提交- 原理:通过TransactionTemplate显式开启事务,精确控制提交时机,避免自动提交导致的延迟。
- 代码示例:public String generateUniqueId() { return transactionTemplate.execute(status -> { // 生成编号逻辑 String id = currentDate + "-" + sequenceService.getNextSequence(); // 显式提交(若未抛异常) return id; });}
- 适用场景:需要更细粒度控制事务的复杂业务逻辑。
- 优势:
灵活性高,可自定义提交/回滚逻辑;
避免隐式提交的不可预测性。
- 局限:
代码复杂度增加;
仍需依赖数据库序列或锁机制保证唯一性。
三、进阶方案:分布式协调服务若事务优化无法满足需求,可引入以下分布式组件:
1. Redis原子操作- 原理:利用局御Redis的INCR或INCRBY命令生成自增序列,结合日期前缀确保唯一性。
- 代码示例:public String generateUniqueIdWithRedis() { String dateKey = "id_prefix:" + LocalDate.now().toString(); Long sequence = redisTemplate.opsForValue().increment(dateKey); return dateKey + ":" + sequence;}
- 优势:
原子性操作,无并发问题;
轻量级,适合高并发场景。
- 局限:
需处理Redis故障时的降级逻辑;
序列号可能溢出(需设计合理长度)。
2. 雪花算法(Snowflake)- 原理:将64位ID划分为时间戳、机器ID和序列号三部分,确保分布式环境下唯一性。
- 结构示例:0 | 时间戳(41位) | 机器ID(10位)汪腊兄 | 序列号(12位)
- 优势:
不依赖数据库或Redis;
趋势递增,适合索引优化。
- 局限:
需预先分配机器ID;
时钟回拨可能导致ID重复(需额外处理)。
3. Zookeeper序列节点- 原理:利用Zookeeper的CREATE_SEQ接口创建临时有序节点,节点名称自动包含唯一序列号。
- 代码示例:public String generateUniqueIdWithZk() throws Exception { String path = zkClient.create("/ids/seq-", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); return path.substring(path.lastIndexOf('-') + 1);}
- 优势:
强一致性,适合金融等高可靠场景;
无需关心机器时间同步。
- 局限:
性能较低,依赖Zookeeper集群稳定性。
四、综合建议优先解决时间同步问题
部署NTP服务,确保集群节点时间偏差小于1秒;
避免在编号中直接使用日期,改用时间戳或逻辑时钟。
根据场景选择方案
低并发:事务优化(方法1/2) + 数据库序列;
高并发:Redis原子操作或雪花算法;
强一致:Zookeeper序列节点。
多级防重机制
生成编号后,通过数据库唯一索引或Redis缓存二次校验;
捕获重复异常时,采用重试或降级策略(如生成随机后缀)。
通过上述方法,可有效避免服务器集群下的重复编号问题,具体选择需权衡性能、一致性和复杂度。