摘要:本篇博文是“Java秒杀系统实战系列文章”的第七篇,在本博文中我们将重点介绍 “在高并发,如秒杀的业务场景下如何生成全局唯一、趋势递增的订单编号”,我们将介绍两种方法,一种是传统的采用随机数生成的方式,另外一种是采用当前比较流行的“分布式唯一ID生成算法-雪花算法”来实现。
内容:在上一篇博文,我们完成了商品秒杀业务逻辑的代码实战,在该代码中,我们还实现了“当用户秒杀成功后,需要在数据库表中为其生成一笔秒杀成功的订单记录”的功能,其对应的代码如下所示:
//通用的方法-记录用户秒杀成功后生成的订单-并进行异步邮件消息的通知
private void commonRecordKillSuccessInfo(ItemKill kill, Integer userId) throws Exception{
//TODO:记录抢购成功后生成的秒杀订单记录
ItemKillSuccess entity=new ItemKillSuccess();
//此处为订单编号的生成逻辑
String orderNo=String.valueOf(snowFlake.nextId());
//entity.setCode(RandomUtil.generateOrderCode()); //传统时间戳+N位随机数
entity.setCode(orderNo); //雪花算法
entity.setItemId(kill.getItemId());
entity.setKillId(kill.getId());
entity.setUserId(userId.toString());
entity.setStatus(SysConstant.OrderStatus.SuccessNotPayed.getCode().byteValue());
entity.setCreateTime(DateTime.now().toDate());
//TODO:学以致用,举一反三 -> 仿照单例模式的双重检验锁写法
if (itemKillSuccessMapper.countByKillUserId(kill.getId(),userId) <= 0){
int res=itemKillSuccessMapper.insertSelective(entity);
//其他逻辑省略
}
}
在该实现逻辑中,其核心要点在于“在高并发的环境下,如何高效的生成订单编号”,那么如何才算是高效呢?Debug认为应该满足以下两点:
(1)保证订单编号的生成逻辑要快、稳定,减少时延
(2)要保证生成的订单编号全局唯一、不重复、趋势递增、有时序性
下面,我们采用两种方式来生成“订单编号”,并自己写一个多线程的程序模拟生成的订单编号是否满足条件。
值得一提的是,为了能直观的观察多线程并发生成的订单编号是否具有唯一性、趋势递增,在这里Debug借助了一张数据库表 random_code 来存储生成的订单编号,其DDL如下所示:
CREATE TABLE `random_code` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_code` (`code`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
从该数据库表数据结构定义语句中可以看出,我们设定了 订单编号字段code 为唯一!所以如果高并发多线程生成的订单编号出现重复,那么在插入数据库表的时候必然会出现错误
下面,首先开始我们的第一种方式吧:基于随机数的方式生成订单编号