之前一直在思考高并发环境下怎样生成唯一订单号,考虑过时间戳、UUID等,但都不是十分满意,直到最近看到公司的订单号的生成方式,感觉还是比较完美的一种解决方式。在这里记录一下公司的订单号的生成方式。
public static long getOrderId() {
//设置订单号前缀(可以根据数据环境/地区的不同来设置不同订单号前缀) String ordernoIndex = PropertiesManager.getPropertiesValue("key");//从配置中心获取订单号前缀 if(StringUtils.isBlank(ordernoIndex)){ ordernoIndex = ""; }
//从redis中获取订单号 long orderId = JedisManager.incr(REDIS_ORDER_KEY, CART_REDIS_POOL);
//判断订单号是否小于订单号初始值 if(orderId < orderIdInitValue){
//设置redis中orderNo的初始值 JedisManager.setString(REDIS_ORDER_KEY, String.valueOf(orderIdInitValue), 0, CART_REDIS_POOL); orderId = JedisManager.incr(REDIS_ORDER_KEY, CART_REDIS_POOL); }
//拼接订单号(订单前缀+redis中的自增长值),并返回 return Long.valueOf(new StringBuffer().append(ordernoIndex).append(String.valueOf(orderId)).toString()); }
订单前缀可以设置在订单中心或配置文件里,这样可以在不同环境获得不同的订单号,避免因不同数据中心,导致出现订单号重复的情况。
JedisManager.incr()方法,该方法是订单号生成的一个亮点,也是支持能够高并发的主要原因。
Incr 命令会将 redis的key 中储存的数字值增一。
decr 命令会将 redis的key 中存储的数字值减一。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR / DECR 操作。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
之前看到过说 Incr 命令最高支持每秒1000万级别的递增(没有测试过),且该命令支持原子性,用来生成订单号来说还是比较轻松的。
同样的该方式也适用于 ”秒杀“库存的递减 等场景。