
臭味相投的朋友们我在这里:猿in小站:http://www.yuanin.netcsdn博客:https://blog.csdn.net/jiabeis简书:https://www.jianshu.com/u/4cb7d664ec4b微信免费订阅号“猿in”互联网快速发展的今天分布式应用系统已经见怪不怪在分布式系统中我们需要各种各样的ID既然是ID那么必然是要保证全局唯一除此之外不同当业务还需要不同的特性比如像并发巨大的业务要求ID生成效率高吞吐大比如某些银行类业务需要按每日日期制定交易流水号又比如我们希望用户的ID是随机的无序的纯数字的且位数长度是小于10位的。等等不同的业务场景需要的ID特性各不一样于是衍生了各种ID生成器。本文讲的订单号就是其中一种业务id下面结合订单业务需求介绍订单号的生成策略。业务需求订单号不能重复订单号没有规则即编码规则不能加入任何和公司运营相关的数据外部人员无法通过订单ID猜测到订单量。不能被遍历。订单号长度固定且不能太长易读易沟通不要出现数字字母换乱现象生成耗时关于订单号的生成一些比较简单的方案1、数据库自增长ID优势无需编码缺陷大表不能做水平分表否则插入删除时容易出现问题高并发下插入数据需要加入事务机制在业务操作父、子表关联表插入时先要插入父表再插入子表2、时间戳随机数优势编码简单缺陷随机数存在重复问题即使在相同的时间戳下。每次插入数据库前需要校验下是否已经存在相同的数值。3、时间戳会员ID优势同一时间一个用户不会存在两张订单缺陷会员ID也会透露运营数据鸡生蛋蛋生鸡的问题例如:SyyMMddHHmmssMath.abs(memberId.hashCode());[说明memberId为uuid的String的hashCode唯一而hashcode可能为负数]4、GUID/UUID优势简单劣势用户不友好索引关联效率较低。UUID全称Universally Unique Identifier即通用唯一识别码。UUID是由一组32位数的16进制数字所构成所以理论上UUID的总数为16^322^128约等于3.4*10^38。也就是书偶偶每纳秒产生1兆个UUID要花100亿年才会将所有UUID用完。UUID的标准形式包含32个16进制数字以连字号分为五段形式为8-4-4-4-12的32个字符如550e8400-e19b-41d4-a716-446655440000。UUID的作用UUID是让分布式系统中的所有元素都能有唯一的辨识信息而不要要通过中央控制端来做辨识信息的指定。如此一来每个人都可以创建不与其他人冲突的UUID。在这样的情况下就不需考虑数据库创建时的名称重复问题。目前最广泛应用的UUID是微软公司的全局唯一标识符GUID而其他重要的应用则有Linux ext2/ext3文件系统、LULS加密分区、GNOME、KDE、Mac OS X等等。UUID的组成UUID是指在一台机器上生成的数字它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成的API。按照开放软件基金会OSF制定的标准计算用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。UUID由以下几部分的组合当前日期和时间UUID的第一个部分与时间有关如果你在生成一个UUID之后过几秒后又生成了一个UUID则第一个部分不同其余相同。时钟序列。全局唯一的IEEE机器识别号如果有网卡从网卡MAC地址获得没有网卡以其他方式获得。UUID的唯一缺陷在于生成的结果穿会比较长。关于UUID这个标准使用最普遍的是微软的GUIDGlobals Ujique Identifiers。5、twitter的SnowFlake [参考:https://blog.csdn.net/li396864285/article/details/54668031]Twitter-Snowflake算法产生的背景相当简单为了满足Twitter每秒上万条消息的请求每条消息都必须分配一条唯一的id这些id还需要一些大致的顺序方便客户端排序并且在分布式系统中不同机器产生的id必须不同.Snowflake算法核心把时间戳工作机器id序列号(毫秒级时间41位机器ID 10位毫秒内序列12位)组合在一起。在上面的字符串中第一位为未使用实际上也可作为long的符号位接下来的41位为毫秒级时间然后5位datacenter标识位5位机器ID并不算标识符实际是为线程标识然后12位该毫秒内的当前毫秒内的计数加起来刚好64位为一个Long型。除了最高位bit标记为不可用以外其余三组bit占位均可浮动看具体的业务需求而定。默认情况下41bit的时间戳可以支持该算法使用到2082年10bit的工作机器id可以支持1023台机器序列号支持1毫秒产生4095个自增序列id。下文会具体分析。这里时间戳的细度是毫秒级具体代码如下建议使用64位linux系统机器因为有vdsogettimeofday()在用户态就可以完成操作减少了进入内核态的损耗。1.snowflake简介snowflake算法是一款本地生成的ID生成过程不依赖任何中间件无网络通信保证ID全局唯一并且ID总体有序递增性能每秒生成300w。2.snowflake算法原理snowflake生产的ID是一个18位的long型数字二进制结构表示如下(每部分用-分开):0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000 - 00000 - 00000000 0000第一位未使用接下来的41位为毫秒级时间(41位的长度可以使用69年从1970-01-01 08:00:00)然后是5位datacenterId最大支持2^532个二进制表示从00000-11111也即是十进制0-31和5位workerId最大支持2^532个原理同datacenterId所以datacenterId*workerId最多支持部署1024个节点最后12位是毫秒内的计数12位的计数顺序号支持每个节点每毫秒产生2^124096个ID序号.所有位数加起来共64位恰好是一个Long型转换为字符串长度为18.单台机器实例通过时间戳保证前41位是唯一的分布式系统多台机器实例下通过对每个机器实例分配不同的datacenterId和workerId避免中间的10位碰撞。最后12位每毫秒从0递增生产ID再提一次每毫秒最多生成4096个ID每秒可达4096000个。理论上只要CPU计算能力足够单机每秒可生产400多万个实测300w效率之高由此可见。该节改编自http://www.cnblogs.com/relucent/p/4955340.html3.snowflake算法源码java版ToStringSlf4jpublicclassSnowflakeIdFactory{privatefinallongtwepoch 1288834974657L;privatefinallongworkerIdBits 5L;privatefinallongdatacenterIdBits 5L;privatefinallongmaxWorkerId -1L^ (-1L workerIdBits);privatefinallongmaxDatacenterId -1L^ (-1L datacenterIdBits);privatefinallongsequenceBits 12L;privatefinallongworkerIdShift sequenceBits;privatefinallongdatacenterIdShift sequenceBits workerIdBits;privatefinallongtimestampLeftShift sequenceBits workerIdBits datacenterIdBits;privatefinallongsequenceMask -1L^ (-1L sequenceBits);privatelongworkerId;privatelongdatacenterId;privatelongsequence 0L;privatelonglastTimestamp -1L;publicSnowflakeIdFactory(longworkerId,longdatacenterId) {if(workerId maxWorkerId || workerId 0) {thrownewIllegalArgumentException(String.format(worker Id cant be greater than %d or less than 0, maxWorkerId));}if(datacenterId maxDatacenterId || datacenterId 0) {thrownewIllegalArgumentException(String.format(datacenter Id cant be greater than %d or less than 0, maxDatacenterId));}this.workerId workerId;this.datacenterId datacenterId;}publicsynchronizedlongnextId() {longtimestamp timeGen();if(timestamp lastTimestamp) {//服务器时钟被调整了,ID生成器停止服务.thrownewRuntimeException(String.format(Clock moved backwards. Refusing to generate id for %d milliseconds, lastTimestamp - timestamp));}if(lastTimestamp timestamp) {sequence (sequence 1) sequenceMask;if(sequence 0) {timestamp tilNextMillis(lastTimestamp);}}else{sequence 0L;}lastTimestamp timestamp;return((timestamp - twepoch) timestampLeftShift) | (datacenterId datacenterIdShift) | (workerId workerIdShift) | sequence;}protectedlongtilNextMillis(longlastTimestamp) {longtimestamp timeGen();while(timestamp lastTimestamp) {timestamp timeGen();}returntimestamp;}protectedlongtimeGen() {returnSystem.currentTimeMillis();}publicstaticvoidtestProductIdByMoreThread(intdataCenterId,intworkerId,intn)throwsInterruptedException {ListThread tlist newArrayList();SetLong setAll newHashSet();CountDownLatch cdLatch newCountDownLatch(10);longstart System.currentTimeMillis();intthreadNo dataCenterId;MapString,SnowflakeIdFactory idFactories newHashMap();for(inti0;i10;i){//用线程名称做map key.idFactories.put(snowflakei,newSnowflakeIdFactory(workerId, threadNo));}for(inti0;i10;i){Thread temp newThread(newRunnable() {Overridepublicvoidrun() {SetLong setId newHashSet();SnowflakeIdFactory idWorker idFactories.get(Thread.currentThread().getName());for(intj0;jn;j){setId.add(idWorker.nextId());}synchronized(setAll){setAll.addAll(setId);log.info({}生产了{}个id,并成功加入到setAll中.,Thread.currentThread().getName(),n);}cdLatch.countDown();}},snowflakei);tlist.add(temp);}for(intj0;j10;j){tlist.get(j).start();}cdLatch.await();longend1 System.currentTimeMillis() - start;log.info(共耗时:{}毫秒,预期应该生产{}个id, 实际合并总计生成ID个数:{},end1,10*n,setAll.size());}publicstaticvoidtestProductId(intdataCenterId,intworkerId,intn){SnowflakeIdFactory idWorker newSnowflakeIdFactory(workerId, dataCenterId);SnowflakeIdFactory idWorker2 newSnowflakeIdFactory(workerId1, dataCenterId);SetLong setOne newHashSet();SetLong setTow newHashSet();longstart System.currentTimeMillis();for(inti 0; i n; i) {setOne.add(idWorker.nextId());//加入set}longend1 System.currentTimeMillis() - start;log.info(第一批ID预计生成{}个,实际生成{}个*共耗时:{},n,setOne.size(),end1);for(inti 0; i n; i) {setTow.add(idWorker2.nextId());//加入set}longend2 System.currentTimeMillis() - start;log.info(第二批ID预计生成{}个,实际生成{}个*共耗时:{},n,setTow.size(),end2);setOne.addAll(setTow);log.info(合并总计生成ID个数:{},setOne.size());}publicstaticvoidtestPerSecondProductIdNums(){SnowflakeIdFactory idWorker newSnowflakeIdFactory(1,2);longstart System.currentTimeMillis();intcount 0;for(inti 0; System.currentTimeMillis()-start1000; i,counti) {/** 测试方法一: 此用法纯粹的生产ID,每秒生产ID个数为300w */idWorker.nextId();/** 测试方法二: 在log中打印,同时获取ID,此用法生产ID的能力受限于log.error()的吞吐能力.* 每秒徘徊在10万左右. *///log.error({},idWorker.nextId());}longend System.currentTimeMillis()-start;System.out.println(end);System.out.println(count);}publicstaticvoidmain(String[] args) {/** case1: 测试每秒生产id个数?* 结论: 每秒生产id个数300w *///testPerSecondProductIdNums();/** case2: 单线程-测试多个生产者同时生产N个id,验证id是否有重复?* 结论: 验证通过,没有重复. *///testProductId(1,2,10000);//验证通过!//testProductId(1,2,20000);//验证通过!/** case3: 多线程-测试多个生产者同时生产N个id, 全部id在全局范围内是否会重复?* 结论: 验证通过,没有重复. */try{testProductIdByMoreThread(1,2,100000);//单机测试此场景,性能损失至少折半!}catch(InterruptedException e) {e.printStackTrace();}}}测试用例/** case1: 测试每秒生产id个数? * 结论: 每秒生产id个数300w */ //testPerSecondProductIdNums(); /** case2: 单线程-测试多个生产者同时生产N个id,验证id是否有重复? * 结论: 验证通过,没有重复. */ //testProductId(1,2,10000);//验证通过! //testProductId(1,2,20000);//验证通过! /** case3: 多线程-测试多个生产者同时生产N个id, 全部id在全局范围内是否会重复? * 结论: 验证通过,没有重复. */ try { testProductIdByMoreThread(1,2,100000);//单机测试此场景,性能损失至少折半! } catch (InterruptedException e) { e.printStackTrace(); }