大流量下订单号生成方法

5,834 阅读3分钟
原文链接: blog.csdn.net

流量不大的情况下,订单号生成


很久之前写过一篇利用DB生成业务主键的文章,介绍了利用DB来生成唯一的ID。当时便是用这种方式来生成订单号的。只不过拿到ID后,根据订单业务,简单加个前缀而已。

@Service
public class KeyGen{
   @Autowired
   private KeyGenRepository keyGenRepository;
   public long genNo(){
   KeyGen keyGen = new KeyGen();
   keyGenRepository.genarateNo(keyGen );
   //这个就是我们需要的no
   long no = keyGen.getNo();
   }
}
private String generateOrderNo() {
        StringBuffer sbf=new StringBuffer();
        //前缀
        sbf.append("100");
        long no = keyGen.genNo();
        sbf.append(no);
        return sbf.toString();
    }

这种方式用了一段时间,没发现有订单号重复的情况。这种解决方案算是一个基础的思路,再复杂的订单生成规则,如果订单号要包含一个唯一的属性,利用数据库的自增特性是个不错的方案。


大流量下订单号的生成


如果每个小时的订单量非常大,比如说,一个小时有两百万个订单,只用单独一个key_gen表是支持不住的,毕竟写入的压力太大了,影响订单号的生成速度。这个时候可以考虑针对订单号的生成,搞单独的库,并分库,降低insert的压力,提高生成订单号的速度。


分库的规则


有一种做法是根据仓库来映射,比如说,一家电商公司的仓库总共有50个,那么可以进行如下的映射:

warehouse1:数据库0
warehouse2:数据库0
warehouse3:数据库1
warehouse4:数据库1
warehouse5:数据库2
warehouse6:数据库2
warehouse7:数据库3
warehouse8:数据库3
warehouse9:数据库3
warehouse10:数据库4
。。。。。。
。。。。。。
warehouse50:数据库9

将50个仓库映射到【0-9】对应的数据库上,当下单的时候,如果订单对应的仓库的是warehouse1,则映射到数据库0,对应的仓库如果是warehouse10,则对应的数据库4。这样子,订单号的生成的压力便分配到10个数据库上了。

进行分库后,每个分库里都有一张key_gen表。


组装订单号


上面的分库分表,目的是为了生成一个唯一的ID,这个ID是订单号的一部分,生成ID借助了数据库,但是后面组装订单号则完全是业务逻辑操作,无需利用数据库了。

订单号的生成规则各个公司都有自己的要求,举个例子:

时间 + 6位随机数 + 数据库生成的唯一ID+仓库标识

时间的生成可以简单的使用如下代码生成:

SimpleDateFormat formatShort = new SimpleDateFormat("yyMMdd");
Date now = new Date();
String currentDate = formatShort.format(now);

六位的随机数可以借助JAVA的AtomicLong来实现,可以应付并发。

到此订单号完整的生成了。那有没有坑呢?因为进行了分库,每个库都有key_gen,生成的ID只是库内的唯一,多个库则是未必的。比如说,两个订单创建的请求,仓库分别是warehouse1和warehouse3,根据上面的配置规则,分别路由到了数据库0数据库1这两个库,这个时候,就可能产生相同的ID。但是不要忘记,订单号的生成是包含仓库标识的,一个1,一个是3,是不同的,另外还有随机数,所以订单号重复的机会基本不太可能的。