030 流水号的生成
Posted juncaoit
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了030 流水号的生成相关的知识,希望对你有一定的参考价值。
公司的流水号生成规则有一个bug需要修复,顺便查查资料,看看人家都怎么做的,这里稍微整理了一些。
一:第一种方式
1.需求加分析
日期+ long(商家Id+订单类型+主机ID+AtomicInteger),什么意思呢,前面的日期保持不变,后面的将商家Id,订单的类型,主机的Id,AtomicInteger,通过移位与或运算“保存”到一个long类型里面。为什么要这么做?
- 不想把相关信息直接暴露出去。
- 通过流水号可以快速得到相关业务信息,快速定位问题。
- 使用AtomicInteger可提高并发量,降低了冲突。
2.原理
- 符号位,这个不用过的介绍,大家都知道2进制第一位都是符号位,0表示正数1表示负数
- 当前秒数,表述的是当前是当天的第多少秒,每天最多有86400秒,最多占17位
- 商家Id占14位,由于业务涉及到商户,订单也是归为每个商户下面的,假定我们的最多有9999家商户,9999占位是14位,所以我们商户Id占14位,大家根据自身业务的量来决定长度。
- 订单类型,假定我们的订单类型还停留在10种以内,所以我们保留4位,最多支持类型16种,大家同样的根据业务的量来决定
- 服务器的Id,假定服务器数量在10台以内,所以我们保留4位,最多支持16台服务器,大家同样的根据自身服务器的数量来决定。,
- 剩下的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 流水号的生成的主要内容,如果未能解决你的问题,请参考以下文章