上一篇
去年我们电商系统踩了个坑——订单表和用户表竟然出现了重复ID!客服接到投诉说"我的订单详情里怎么显示别人的收货地址",一查发现两个业务系统共用了同一个数据库序列,更糟的是促销期间Redis生成的优惠券ID和库存流水号撞车,导致超发事故,这时候才明白:每个系统都该有自己的身份证号码牌。
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; }
优点:
坑点提醒:
# 初始化各业务线计数器 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); } }
比如生成"ORD2025070100001"格式的订单号:
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集群):
我们现在的生产环境方案:
graph TD A[客户端] -->|获取ID| B{业务类型} B -->|常规业务| C[Redis集群] B -->|敏感业务| D[MySQL多主集群] C --> E[本地号段缓存]
具体实现技巧:
FLUSHDB
,导致生成重复ID user:20250701
代替user
作为key,避免单个key过大 INCR
+EXPIRE
组合 5001201
表示01分片 选择ID生成方案就像选车牌——简单自增像普通车牌够用就好,分布式系统则需要新能源牌照的特殊规则,关键是根据业务场景做好隔离,就像我们后来给订单、用户、物流分别建立了独立的ID池,再没出现过串号问题。好的ID设计应该让系统一眼就能认出"这是自己人"。
本文由 栾芸芸 于2025-07-27发表在【云服务器提供商】,文中图片由(栾芸芸)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://up.7tqx.com/wenda/462819.html
发表评论