030 流水号的生成

Posted juncaoit

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了030 流水号的生成相关的知识,希望对你有一定的参考价值。

  公司的流水号生成规则有一个bug需要修复,顺便查查资料,看看人家都怎么做的,这里稍微整理了一些。

一:第一种方式

1.需求加分析

  日期+ long(商家Id+订单类型+主机ID+AtomicInteger),什么意思呢,前面的日期保持不变,后面的将商家Id,订单的类型,主机的Id,AtomicInteger,通过移位与或运算“保存”到一个long类型里面。为什么要这么做?

  1. 不想把相关信息直接暴露出去。
  2. 通过流水号可以快速得到相关业务信息,快速定位问题。
  3. 使用AtomicInteger可提高并发量,降低了冲突。

 

2.原理

  技术分享图片

  1. 符号位,这个不用过的介绍,大家都知道2进制第一位都是符号位,0表示正数1表示负数
  2. 当前秒数,表述的是当前是当天的第多少秒,每天最多有86400秒,最多占17位
  3. 商家Id占14位,由于业务涉及到商户,订单也是归为每个商户下面的,假定我们的最多有9999家商户,9999占位是14位,所以我们商户Id占14位,大家根据自身业务的量来决定长度。
  4. 订单类型,假定我们的订单类型还停留在10种以内,所以我们保留4位,最多支持类型16种,大家同样的根据业务的量来决定
  5. 服务器的Id,假定服务器数量在10台以内,所以我们保留4位,最多支持16台服务器,大家同样的根据自身服务器的数量来决定。,
  6. 剩下的24位全部留给AtomicInteger,设计上我这里的qps可以达到2的24方。这个其实已经很大了。大家根据上面的设计留下来的数量当AtomicInteger位数,其实可以满足大部分业务需求了。

 

 3.程序大纲

  技术分享图片

 

4.TimeUtil.java

 1 package com.jun.it;
 2 
 3 import java.text.SimpleDateFormat;
 4 import java.util.Calendar;
 5 import java.util.Date;
 6 import java.util.GregorianCalendar;
 7 
 8 public class TimeUtil {
 9     public static final String DATE_FORMAT_PATTERN_DEFAULT = "yyyy-MM-dd HH:mm:ss";
10     public static final String DATE_FORMAT_PATTERN_NO_SECOND = "yyyy-MM-dd HH:mm";
11     public static final String DATE_FORMAT_PATTERN_HTTP = "EEE, dd-MMM-yyyy HH:mm:ss z";
12     public static final String DATE_FORMAT_PATTERN_SHORT = "yyyy-MM-dd";
13     public static final String DATE_FORMAT_PATTERN_INT = "yyyyMMdd";
14     public static final String DATE_FORMAT_PATTERN_NO_SEPARATOR = "yyyyMMddHHmmss";
15     public static final String DATE_FORMAT_PATTERN_MONTH_INT = "yyyyMM";
16 
17     public static int toSeconds() {
18         return (int) (System.currentTimeMillis() / 1000);
19     }
20 
21     public static int toSeconds(Date date) {
22         if (date != null) {
23             return (int) (date.getTime() / 1000);
24         }
25         return 0;
26     }
27 
28 
29     public static Date floor(Date date) {
30         Calendar calendar = new GregorianCalendar();
31         calendar.setTime(date);
32         calendar.set(Calendar.HOUR_OF_DAY, 0);
33         calendar.set(Calendar.MINUTE, 0);
34         calendar.set(Calendar.SECOND, 0);
35         calendar.set(Calendar.MILLISECOND, 0);
36         return calendar.getTime();
37     }
38 
39     public static Date ceiling(Date date) {
40         Calendar calendar = new GregorianCalendar();
41         calendar.setTime(date);
42         calendar.set(Calendar.HOUR_OF_DAY, 23);
43         calendar.set(Calendar.MINUTE, 59);
44         calendar.set(Calendar.SECOND, 59);
45         calendar.set(Calendar.MILLISECOND, 999);
46         return calendar.getTime();
47     }
48 
49     public static String format(Date date, String formatString) {
50         if (date == null) {
51             date = new Date();
52         }
53         return new SimpleDateFormat(formatString).format(date);
54     }
55 
56     public static int toInt(Date date) {
57         if (date != null) {
58             return Integer.parseInt(format(date, DATE_FORMAT_PATTERN_INT));
59         }
60         return 0;
61     }
62 
63     public static Date now() {
64         return new Date();
65     }
66 
67 
68     public static void main(String[] args) {
69         System.out.println(floor(new Date()));
70         System.out.println(ceiling(new Date()));
71         System.out.println(now());
72         System.out.println(toInt(new Date()));
73     }
74 }

 

5.SerialNumberDemo.java

  这里的方式是优雅了许多,是通过移位,然后或的方式拼接而成。

 1 package com.jun.it;
 2 
 3 import java.util.Date;
 4 import java.util.concurrent.atomic.AtomicInteger;
 5 
 6 public class SerialNumberDemo {
 7      private static final AtomicInteger SERIAL = new AtomicInteger(Integer.MAX_VALUE);
 8         private static final int SHIFTS_FOR_TIMESTAMP = 17;
 9         private static final int SHIFTS_FOR_UNION = 14;
10         private static final int SHIFTS_FOR_TYPE = 4;
11         private static final int SHIFTS_FOR_NODE = 4;
12         private static final int SHIFTS_FOR_SERIAL = 24;
13         private static final int MASK_FOR_SERIAL = (1 << SHIFTS_FOR_SERIAL) - 1;
14         private static final long MASK_FOR_UNION = (1 << SHIFTS_FOR_UNION) - 1;
15         private static final long MASK_FOR_TYPE = (1 << SHIFTS_FOR_TYPE) - 1;
16 
17         public static String next(long mechId, long type) {
18             long second = TimeUtil.toSeconds() - TimeUtil.toSeconds(TimeUtil.floor(new Date()));
19             long serverId = 1; //这个地方应该可以根据tomcat参数来设置的,通过或tomcat参数来获取服务器id
20             long serial = SERIAL.incrementAndGet();
21             long secondShift = second << (64 - 1 - SHIFTS_FOR_TIMESTAMP);
22             long unionShift = mechId << (64 - 1 - SHIFTS_FOR_TIMESTAMP - SHIFTS_FOR_UNION);
23             long typeShift = type << (64 - 1 - SHIFTS_FOR_TIMESTAMP - SHIFTS_FOR_UNION - SHIFTS_FOR_TYPE);
24             long nodeShift = serverId << (64 - 1 - SHIFTS_FOR_TIMESTAMP - SHIFTS_FOR_UNION - SHIFTS_FOR_TYPE - SHIFTS_FOR_NODE);
25             long number = secondShift | unionShift | typeShift | nodeShift | (serial & MASK_FOR_SERIAL);
26             return String.valueOf(TimeUtil.toInt(new Date())) + String.valueOf(number);
27         }
28 
29         public static long getSecond(long id) {
30             return id >> (SHIFTS_FOR_UNION + SHIFTS_FOR_TYPE + SHIFTS_FOR_NODE + SHIFTS_FOR_SERIAL);
31         }
32 
33         public static long getMechId(long id) {
34             return (id >> (SHIFTS_FOR_TYPE + SHIFTS_FOR_NODE + SHIFTS_FOR_SERIAL)) & MASK_FOR_UNION;
35         }
36 
37         public static long getType(long id) {
38             return (id >> (SHIFTS_FOR_NODE + SHIFTS_FOR_SERIAL)) & MASK_FOR_TYPE;
39         }
40 
41 
42         public static void main(String[] args) {
43             String number = next(14, 4);
44             System.out.println(number);  
45             System.out.println("秒数:"+getSecond(5709508857563185152L));
46             System.out.println("商户Id:"+getMechId(5709508857563185152L));
47             System.out.println("订单类型:"+getType(5709508857563185152L));
48         }
49 }

 

6.效果

  技术分享图片

 

二:

 

以上是关于030 流水号的生成的主要内容,如果未能解决你的问题,请参考以下文章

js生成流水号自增

怎么生成流水号

java怎样自动生成流水号

Java之流水号生成器

基于Oracle Sequence的流水号生成规则

030.字符排列