当前位置:首页 > 问答 > 正文

自增ID生成器 唯一标识符 使用数据库与Redis实现各系统独立的自增ID生成方案

自增ID生成器实战:用数据库和Redis打造多系统独立ID方案

场景:当你的订单ID和用户ID开始打架

去年我们电商系统踩了个坑——订单表和用户表竟然出现了重复ID!客服接到投诉说"我的订单详情里怎么显示别人的收货地址",一查发现两个业务系统共用了同一个数据库序列,更糟的是促销期间Redis生成的优惠券ID和库存流水号撞车,导致超发事故,这时候才明白:每个系统都该有自己的身份证号码牌

数据库自增ID:老司机的稳妥选择

MySQL的经典玩法

CREATE TABLE `id_generator` (
  `biz_tag` varchar(32) NOT NULL COMMENT '业务标识',
  `max_id` bigint(20) NOT NULL COMMENT '当前最大值',
  `step` int(11) NOT NULL COMMENT '号段长度',
  PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

插入初始化数据:

INSERT INTO id_generator(biz_tag, max_id, step) 
VALUES ('order', 10000, 1000),
       ('user', 50000, 2000);

Java取号逻辑:

public synchronized Long nextId(String bizTag) {
    // 1. 查询当前号段
    IdGenerator generator = dao.selectForUpdate(bizTag);
    // 2. 计算返回ID
    Long currentId = generator.getMaxId();
    generator.setMaxId(currentId + generator.getStep());
    // 3. 更新数据库
    dao.updateGenerator(generator);
    return currentId + 1;
}

优点

自增ID生成器 唯一标识符 使用数据库与Redis实现各系统独立的自增ID生成方案

  • 绝对递增的数字序列
  • 断电不会丢号(InnoDB保证事务持久性)
  • 步长机制减少数据库压力

坑点提醒

  • 用SELECT...FOR UPDATE避免并发冲突
  • 步长设置要考虑业务增长速度(设置过小会导致频繁更新)

Redis方案:高性能ID生产线

基础原子操作版

# 初始化各业务线计数器
127.0.0.1:6379> SET order_id 10000
127.0.0.1:6379> SET user_id 50000
# 获取订单ID(原子递增)
127.0.0.1:6379> INCR order_id

Spring Boot实现示例:

@Repository
public class RedisIdGenerator {
    @Autowired
    private StringRedisTemplate redisTemplate;
    public Long nextId(String bizKey) {
        return redisTemplate.opsForValue().increment(bizKey);
    }
}

带日期前缀的复合ID

比如生成"ORD2025070100001"格式的订单号:

自增ID生成器 唯一标识符 使用数据库与Redis实现各系统独立的自增ID生成方案

public String generateOrderId() {
    // 日期部分
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
    String dateStr = LocalDate.now().format(formatter);
    // 序列部分
    Long seq = redisTemplate.opsForValue()
            .increment("order:" + dateStr);
    return "ORD" + dateStr + String.format("%05d", seq);
}

性能实测(2025年阿里云Redis 6.2集群):

  • 单节点QPS可达8万+/秒
  • 集群模式下不同业务key会自动分散到不同slot

混搭方案:取长补短

我们现在的生产环境方案:

graph TD
    A[客户端] -->|获取ID| B{业务类型}
    B -->|常规业务| C[Redis集群]
    B -->|敏感业务| D[MySQL多主集群]
    C --> E[本地号段缓存]

具体实现技巧:

自增ID生成器 唯一标识符 使用数据库与Redis实现各系统独立的自增ID生成方案

  1. 冷启动预热:系统启动时从数据库加载5000个ID到本地内存
  2. 双buffer机制:当前号段用到30%时异步加载下一个号段
  3. 监控告警:对Redis的INCR操作做速率监控

避坑指南(血泪总结)

  1. 永远不要重置计数器:去年运维同学误操作执行了FLUSHDB,导致生成重复ID
  2. 大key问题:用user:20250701代替user作为key,避免单个key过大
  3. 时钟回拨:服务器时间同步导致基于时间的ID重复,建议使用INCR+EXPIRE组合
  4. 分库分表兼容:用户ID末尾保留2位给分片标识,如5001201表示01分片

选择ID生成方案就像选车牌——简单自增像普通车牌够用就好,分布式系统则需要新能源牌照的特殊规则,关键是根据业务场景做好隔离,就像我们后来给订单、用户、物流分别建立了独立的ID池,再没出现过串号问题。好的ID设计应该让系统一眼就能认出"这是自己人"

发表评论