文档章节

高并发生成订单号

新时代搬砖
 新时代搬砖
发布于 2016/09/29 13:20
字数 1477
阅读 185
收藏 1

银联16位数字订单号
永远不重复的生成算法
  请尊重知识,请尊重原创 更多资料参考请见  http://www.cezuwang.com/listFilm?page=1&areaId=906&filmTypeId=1
1、 前提背景
相信做过银联支付的都知道,银联的订单号要求商户提供一个不重复的16位数字订单号(不重复指的是对商户本身,不用考虑银联有多个商户会与其他商户的订单号重复)。16位数其实很短,要考虑每秒并发1w或者10w或100万时,重复订单号将数不过来。
需要考虑的因素:
 若使用数据库保存流水号,集群部署时,同步关键字不再有效。当然同步对性能也有非常大的影响;
 若使用时间,必须要精确到毫秒、微妙级别,长度就不止16位了。
 若使用数据库字段自增,数据库并发时硬件将吃不消。
 获取订单号时检查表的最大值,这种方案是最不可取的。

以下将给出本人经过深入研究的三种方案,按顺序,最优的方案为第三个。
备注:
如果要测试产生重复订单号的情况,可以建立一个表,把订单号字段设置为唯一性,然后开启1000或10000或更多的线程去请求方法,每个线程循环5次或10次来请求,在方法里面写插入语句。或者可以使用Apache的ab工具并发测试。
使用方法:ab -n5000 -c5000 http://192.168.1.102:8888/kjcx/aaa.action
2、 可选方案一 
本方案使用的是当前时间,包括毫秒数、纳秒数,不需要数据库参与计算,性能不用说。
算法:

Java代码 

OrderId=machineId+(System.currentTimeMillis()+"").substring(1)+(System.nanoTime()+"").substring(7,10);  


讲解:
参数machineId:是集群时的机器代码,可以1-9任意。部署时,分别为部署的项目手动修改该值,以确保集群的多台机器在系统时间上不一致的问题(毫无疑问每台机器的毫秒数基本上不一致)。
参数System.currentTimeMillis():这是java里面的获取1970年到目前的毫秒数,是一个13位数的数字,与Date.getTime()函数的结果一样,比如1378049585093。经过研究,在2013年,前三位是137,在2023年是168,到2033年才199.所以,我决定第一位数字1可以去掉,不要占位置了。可以肯定绝大多数系统用不了10年20年。这样,参数2就变成了12位数的数字,加上参数1machineId才13位数。
参数System.nanoTime():这是java里面的取纳秒数,经过深入研究,在同一毫秒内,位置7,8,9这三个数字是会变化的。所以决定截取这三个数字出来拼接成一个16位数的订单号。
总结:理论上此方案在同一秒内,可以应对1000*1000个订单号,但是经过测试,在每秒并发2000的时候,还是会出现2-10个重复。
3、 可选方案二
本方案使用的是获得会话ID(sessionId)来产生hashCode。
算法:

Java代码 

OrderId=machineId+session().getId().hashCode();  


讲解:
参数machineId不再讲解,与方案一致。
参数2 session().getId().hashCode()是值在web系统中获取用户浏览器与web容器的唯一会话编号,再把该会话ID转换为该字符串的hashCode值,如1939354961。该值可能是一个11位数的或10位数的,或者在前面还会出现-号,也就是有可能该值是负数,没关系,取正。然后再对该值进行左补0到15位数,基本上可以应对位数不一致的问题。
我们知道,hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值。可以想象,hashCode的值如果出现重复,那就是一个值了,而不是不同的值。又因为sessionId是客户端、与浏览器有关联的,所以基本上不会出现重复,但是如果用户在同一个会话有效期内、同一个版本的浏览器,生成2次就无效了,因为会话ID是一致的。
总结:该算法,可以确保不重复的概率很小,但是需要自己特殊处理同会话同浏览器生成1次以上订单号的问题,此算法没有经过调试,略过,您请看方案三。
4、 可选方案三
本方案在基于方案二的基础上做了修改,使用的使用UUID而不是会话id。
UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的,这个不重复性全世界人民都知道。当然,既然字符串值不重复,那对应的hashCode也是一样,不会重复。
算法:

Java代码 

OrderId=machineId+UUID.randomUUID().toString().hashCode();  



讲解:
参数1不再解释。
参数2是值生成UUID然后取它的hashCode值,经过测试,完全没有一点问题。您可以开1000w的并发去测试插入吧,只要数据库不会报唯一性错误,那就没问题。
总结:
hashCode这个算法从搞软件开始到现在这么多年,一直没派上用场,这次大大的用上了。解决了问题。请同志们以后善用这个东西。
5、 附录:方案三的算法代码

Java代码

public static String getOrderIdByUUId() {  
    int machineId = 1;//最大支持1-9个集群机器部署  
    int hashCodeV = UUID.randomUUID().toString().hashCode();  
    if(hashCodeV < 0) {//有可能是负数  
        hashCodeV = - hashCodeV;  
    }  
    // 0 代表前面补充0       
    // 15 代表长度为15     
    // d 代表参数为正数型  
    return machineId+String.format("%015d", hashCodeV);  
}  


方案三其实也就一个函数,很简便。

© 著作权归作者所有

新时代搬砖
粉丝 9
博文 17
码字总数 4979
作品 0
汕头
程序员
私信 提问
加载中

评论(4)

新时代搬砖
新时代搬砖 博主

引用来自“干爷爷”的评论

三方案:并发10,多执行几次就会有重复

引用来自“小好人lucky”的评论

最近找了个IdWorker.

引用来自“干爷爷”的评论

求分享,目前找不到可行的
http://note.youdao.com/noteshare?id=db5b19a0f02600a47b11d12843f867e8
干爷爷
干爷爷

引用来自“干爷爷”的评论

三方案:并发10,多执行几次就会有重复

引用来自“小好人lucky”的评论

最近找了个IdWorker.
求分享,目前找不到可行的
新时代搬砖
新时代搬砖 博主

引用来自“干爷爷”的评论

三方案:并发10,多执行几次就会有重复
最近找了个IdWorker.
干爷爷
干爷爷
三方案:并发10,多执行几次就会有重复
高并发下怎样生成唯一的订单号?

高并发下怎样生成唯一的订单号?比如像银行的订单号根据时间生成,mysql数据库。

chaun
2015/10/17
33.7K
16
java web系统在高并发和分布式下如何实现订单号生成唯一

方案一:如果没有并发,订单号只在一个线程内产生,那么由于程序是顺序执行的,不同订单的生成时间戳正常不同,因此用时间戳+随机数(或自增数)就可以区分各个订单。如果存在并发,且订单号...

刘诗书
2017/11/22
0
0
如何理解和处理并发下的这些问题,你知道吗?

简介:这几天看并发,看的越来越糊涂,并发中最容易出问题的就是锁。 语言:JAVA 问题描述: 一、 在java 里,程序并发时,加锁,在文档,博客,书籍中,凡是说到并发,都会说一个东西,锁。j...

god_share
2015/03/13
772
3
java生成12位数字订单号,高并发下

高并发下(每秒1万次),如何用java生成12位数字订单号???

白饼x
2017/02/21
5.9K
17
ZooKeeper典型使用场景

先需要下载 curator 的依赖包 curator-framework curator-recipes Guava-14.0.1 分布式锁 在分布式环境中,为了保证数据的一致性,经常在程序的某个运行点需要进行同步控制。 类是一个同步计...

兔之
2015/10/29
242
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring Security 自定义登录认证(二)

一、前言 本篇文章将讲述Spring Security自定义登录认证校验用户名、密码,自定义密码加密方式,以及在前后端分离的情况下认证失败或成功处理返回json格式数据 温馨小提示:Spring Security...

郑清
35分钟前
3
0
php yield关键字以及协程的实现

php的yield是在php5.5版本就出来了,而在初级php界却很少有人提起,我就说说个人对php yield的理解 Iterator接口 在php中,除了数组,对象可以被foreach遍历之外,还有另外一种特殊对象,也就是继承...

冻结not
48分钟前
4
0
servlet请求和响应的过程

本文转载于:专业的前端网站➥servlet请求和响应的过程 1.加载 Servlet类被加载到Java虚拟机中,并且实例化。在这个过程中,web容器(例如tomcat)会调用Servlet类的公开无参构造函数,产生一...

前端老手
49分钟前
4
0
golang 1.13 errors 包来了,不用写“err 气功波”代码

引 这篇是对 errors 包 的姿势挖掘 气功波错误代码 从 http.Get()返回的错误 判断 syscall.ECONNREFUSED 错误.以前要对 go 标准库 error 结构有点熟悉,才能写出下面的代码 func CmdErr(err ...

guonaihong
52分钟前
28
0
喜玛拉雅已听书单

时间倒序排 书名 作者 状态 唐砖 孑与2 进行中 死灵之书(克苏鲁神话合集) 阿卜杜拉·阿尔哈萨德 进行中 赡养人类 刘慈欣 完结 赡养上帝 刘慈欣 完结 中国太阳 刘慈欣 完结 中国太阳 刘慈欣...

Alex_Java
53分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部