Java generates unique identifiers in three ways

preface

We often encounter such scene, need to generate a unique serial number to show the uniqueness of a certain data, in the application of single node we can simply use a integer to implement on the implementation, but in the case of distributed this way the possibility of a conflict, so is there any way we can generate a unique serial number? And what if you want the serial number to show some business information as well?

The body of the

UUID Specifies the unique identifier of the UUID

The purpose of UUID is to allow all elements in a distributed system to have unique identification information, without the need for a central controller to specify identification information. This way, each person can create a UNIVERSAL unique identifier (UUID) that does not conflict with others.

UUID components:

  1. Current date and time.
  2. Clock sequence (a sequence of values in the order in which they occur)
  3. Globally uniqueIEEEMachine identification number.

UUID is guaranteed to be globally unique, but it takes up 32 bits (decimal), is unordered, and has poor performance when imported into the library. This is because the index structure of a relational database is B+ tree, if we increase the order of ID, new nodes will be inserted after the last node, when the last node is full, new nodes will be split. However, if the insertion is disordered, it will not only lead to fission of intermediate nodes, but also produce many unsaturated nodes, resulting in performance degradation.

Generated UUIDStringUtils:UUIDA utility class with a unique identifier

public class UUIDStringUtils {
    public static String randomUUID(a) {
        UUID uuid = UUID.randomUUID();
        return uuid.toString().replace("-"."").toUpperCase(); }}Copy the code

Validation method

    @org.junit.Test
    public void test(a){
        System.out.println("Unique serial number generated by way of UUID"+UUIDStringUtils.randomUUID());
    }
Copy the code

The running results are as follows:

SnowFlake implements a unique identifier

The SnowFlake algorithm: generates globally unique ids in an increasing trend in distributed systems.

It is 64 bits in base 2 and 18 bits in decimal

  • Part 1:1 bit, always 0.
  • Part 2:41-bit, millisecond accurate timestamp.
  • Part three: 10 bits, machine code
  • Part four: 12 bits, serial number

Advantages of Snowflake:

  • Is in accordance with theIDIncremental, easy to insert into the database
  • Independent of database, in memory generation, good performance

Generated SnowflakeIdWorker:SnowFlakeA utility class with a unique identifier

public class SnowflakeIdWorker {
    // ==============================Fields===========================================
    /** Start time (2015-01-01) */
    private final long twepoch = 1420041600000L;
    /** The number of digits in the machine ID */
    private final long workerIdBits = 5L;
    /** The number of digits in the data id */
    private final long datacenterIdBits = 5L;
    /** The maximum machine ID supported, resulting in 31 (this shift algorithm can quickly calculate the maximum decimal number represented by several binary digits) */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    /** The maximum data id supported. The result is 31 */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    /** The number of digits in the sequence */
    private final long sequenceBits = 12L;
    /** The machine ID is shifted 12 bits to the left */
    private final long workerIdShift = sequenceBits;
    /** Data id is shifted 17 bits to the left (12+5) */
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    /** The time warp moves 22 bits to the left (5+5+12) */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    /** Generate the mask of the sequence, here 4095 (0b111111111111= 0xFFf =4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    /** ID of the working machine (0~31) */
    private long workerId;
    /** DATA center ID(0-31) */
    private long datacenterId;
    /** Milliseconds sequence (0~4095) */
    private long sequence = 0L;
    /** The last time the ID was generated */
    private long lastTimestamp = -1L;
    //==============================Constructors=====================================
    /** * constructor *@paramWorkerId Indicates the work ID (0 to 31) *@paramDatacenterId datacenterId (0 to 31) */
    public SnowflakeIdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
    // ==============================Methods==========================================
    /** * get the next ID (this method is thread-safe) *@return SnowflakeId
     */
    public synchronized long nextId(a) {
        long timestamp = timeGen();
        // If the current time is less than the last timestamp generated by the ID, an exception should be thrown when the system clock is rolled back
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        // If they are generated at the same time, the sequence is performed in milliseconds
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            // Sequence overflow in milliseconds
            if (sequence == 0) {
                // block until the next millisecond to get a new timestamptimestamp = tilNextMillis(lastTimestamp); }}// The timestamp changes and the sequence is reset in milliseconds
        else {
            sequence = 0L;
        }
        // The last time the ID was generated
        lastTimestamp = timestamp;
        // Shift and put together by or to form a 64-bit ID
        return ((timestamp - twepoch) << timestampLeftShift) //
                | (datacenterId << datacenterIdShift) //
                | (workerId << workerIdShift) //
                | sequence;
    }
    /** * blocks to the next millisecond until a new timestamp * is obtained@paramLastTimestamp Specifies the time when the ID was last generated *@returnCurrent timestamp */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    /** * returns the current time in milliseconds *@returnCurrent time (ms) */
    protected long timeGen(a) {
        return System.currentTimeMillis();
    }
    //==============================Test=============================================
    / * * * / test
    public static void main(String[] args) {
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0.0);
        for (int i = 0; i < 1000; i++) {
            long id = idWorker.nextId();
            // Convert to binarySystem.out.println(Long.toBinaryString(id)); System.out.println(id); }}}Copy the code

Validation method

    @org.junit.Test
    public void test2(a){
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0.0);
        long id = idWorker.nextId();
        System.out.println("Unique serial number generated by SnowFlake."+Long.toBinaryString(id) );
    }
Copy the code

The running results are as follows:

Generate a unique identifier with a business identifier through the time tool

In fact, in many cases where there is not so much concurrency and we need to get some simple business information from a unique identifier, we can use Java’s built-in time utility class to generate a unique identifier with a business identifier.

OrderNoUtils: A utility class that generates a unique identifier with a business identifier

public class OrderNoUtils {

    private OrderNoUtils(a) {
        throw new IllegalStateException("Utility class");
    }

    /** * generate order number **@paramProductNo * Mobile phone number *@return* /
    public static String createOrderNo(String productNo) {
        return DateTimeUtils.getTodayChar14() + productNo
                + RandomNumberUtil.createRandomNumber(6);
    }


    / * * *@paramProductNo * Mobile phone number *@return* /
    public static String createVipBizNo(String productNo) {
        return "VIP" + DateTimeUtils.getTodayChar14() + productNo
                + RandomNumberUtil.createRandomNumber(4);
    }

    /** * generate serial number *@param identifier
     * @param randomLength
     * @return* /
    public static String createRequestNo(String identifier,int randomLength) {
        returnDateTimeUtils.getTodayChar17() + identifier + RandomNumberUtil.createRandomNumber(randomLength); }}Copy the code

RandomNumberUtil: Utility class that generates random numbers


public class RandomNumberUtil {
    private RandomNumberUtil(a) {}public static String createRandomNumber(int length) {
        StringBuilder strBuffer = new StringBuilder();
        Random rd = new Random();
        for (int i = 0; i < length; i++) {
            strBuffer.append(rd.nextInt(10));
        }
        returnstrBuffer.toString(); }}Copy the code

DateTimeUtils: Time utility class

public class DateTimeUtils {

    // private static Logger logger = LoggerFactory.getLogger(DateTimeUtils.class);

    private static List<String> dateFormatPattern = new ArrayList<String>();

    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat();

    static {
        dateFormatPattern.add("yyyy-MM-dd HH:mm:ss");
        dateFormatPattern.add("yyyy-MM-dd HH:mm:ss.S z");
        dateFormatPattern.add("yyyy-MM-dd G HH:mm:ss.S z");
        dateFormatPattern.add("yyyy-MM-dd HH:mm:ss.S 'UTC'");
        dateFormatPattern.add("yyyy-MM-dd G HH:mm:ss.S 'UTC'");
        dateFormatPattern.add("yyyy-MM-dd HH:mm:ss.S z");
        dateFormatPattern.add("yyyy-MM-dd HH:mm:ss.S a");
        dateFormatPattern.add("yyyy-MM-dd HH:mm:ssz");
        dateFormatPattern.add("yyyy-MM-dd HH:mm:ss z");
        dateFormatPattern.add("yyyy-MM-dd HH:mm:ss 'UTC'");
        dateFormatPattern.add("yyyy-MM-dd'T'HH:mm:ss.SX");
        dateFormatPattern.add("yyyy-MM-dd'T'HH:mm:ssX");
        dateFormatPattern.add("yyyy-MM-dd'T'HH:mmX");
        dateFormatPattern.add("yyyy-MM-dd HH:mm:ssa");
        dateFormatPattern.add("yyyy/MM/dd");
        dateFormatPattern.add("yyyy/M/d");
        dateFormatPattern.add("yyyy-MM-dd");
        dateFormatPattern.add("yyyy-M-d");
        dateFormatPattern.add("yyyy/M/d");
        dateFormatPattern.add("Yyyy, M, D");
        dateFormatPattern.add("Yyyy year MM month DD day");
        dateFormatPattern.add("yyyy-MM-dd'T'HH:mm:ss.SSS+0800");
    }

    // Format: Chinese week
    private final static String[] FORMAT_WEEK_CHINESE_SIMPLE = {"Sunday"."On Monday"."Tuesday"."On Wednesday"."Thursday"."Friday"."Saturday"};
    // Format: Chinese week
    private final static String[] FORMAT_WEEK_CHINESE = {"Sunday"."Monday"."Tuesday"."Wednesday"."Thursday"."Friday"."Saturday"};
    // Format: Short for English format
    private final static String[] FORMAT_WEEK_ENGLISH_SIMPLE = {"Sunday"."Monday"."Tuesday"."Wednesday"."Thursday"."Friday"."Saturday"};
    // Format: Full English name
    private final static String[] FORMAT_WEEK_ENGLISH = {"Sun"." Mon"." Tue"." Wed"." Thu"." Fri"." Sat"};

    /** * get the current year **@return yyyy
     */
    public static String getCurrentYear(a) {
        return DateFormatUtils.format(new Date(), "yyyy");
    }

    /** * get today's month **@return MM
     */
    public static String getCurrentMonth(a) {
        return DateFormatUtils.format(new Date(), "MM");
    }

    /** ** get today's date **@return dd
     */
    public static String getTodayDay(a) {
        return DateFormatUtils.format(new Date(), "dd");
    }


    /** * return to date **@return yyyyMM
     */
    public static String getTodayChar6(a) {
        return DateFormatUtils.format(new Date(), "yyyyMM");
    }

    /** * returns year **@return yyyyMMdd
     */
    public static String getTodayChar8(a) {
        return DateFormatUtils.format(new Date(), "yyyyMMdd");
    }

    /** ** returns year month day small hour **@return yyyyMMddHHmm
     */
    public static String getTodayChar12(a) {
        return DateFormatUtils.format(new Date(), "yyyyMMddHHmm");
    }

    /** * returns year month day hour minute second **@return yyyyMMddHHmmss
     */
    public static String getTodayChar14(a) {
        return DateFormatUtils.format(new Date(), "yyyyMMddHHmmss");
    }

    /** * returns year month day hour minute second millisecond **@return yyyyMMddHHmmssS
     */
    public static String getTodayChar17(a) {
        String dateString = DateFormatUtils.format(new Date(), "yyyyMMddHHmmssS");
        int length = dateString.length();

        if (length < 17) {
            String endStr = dateString.substring(14, length);
            int len = endStr.length();
            for (int i = 0; i < 3 - len; i++) {
                endStr = "0" + endStr;
            }
            dateString = dateString.substring(0.14) + endStr;
        }
        return dateString;
    }

    /** * returns the current timestamp of the local system **@return* /
    public static long getSysCurrentTimeMillis(a) {
        return System.currentTimeMillis();
    }

    /** * Date format conversion **@param timeMillis
     * @param format
     * @return* /
    public static String convertTimeFormat(long timeMillis, String format) {
        return DateFormatUtils.format(timeMillis, format);
    }

    /** * Returns the current system time **@return yyyy-MM-dd HH:mm:ss
     */
    public static String getSystemTime(a) {
        Calendar theCa = Calendar.getInstance();
        theCa.setTime(new Date());
        return DateFormatUtils.format(theCa.getTime(), "yyyy-MM-dd HH:mm:ss");
    }

    /** * get what day is today **@paramType The default value is 1: Chinese (Monday). 2: Chinese (Monday); 3: Abbreviation (Mon); 4: English full name (Monday) *@return* /
    public static String getWeekName(int type) {
        String strResult = "";
        try {
            Calendar calendar = Calendar.getInstance();
            int intWeekNum = calendar.get(Calendar.DAY_OF_WEEK);
            intWeekNum = intWeekNum - 1;
            if (type == 1) {
                strResult = FORMAT_WEEK_CHINESE_SIMPLE[intWeekNum];
            } else if (type == 2) {
                strResult = FORMAT_WEEK_CHINESE[intWeekNum];
            } else if (type == 3) {
                strResult = FORMAT_WEEK_ENGLISH_SIMPLE[intWeekNum];
            } else if (type == 4) {
                strResult = FORMAT_WEEK_ENGLISH[intWeekNum];
            } else{ strResult = FORMAT_WEEK_CHINESE_SIMPLE[intWeekNum]; }}catch (Exception ex) {
            strResult = "";
        }
        return strResult;
    }

    /** * The number of days in the current month **@return* /
    public static int getCurrentMonthDays(a) {
        Calendar a = Calendar.getInstance();
        a.set(Calendar.DATE, 1);// Set the date to the first day of the month
        a.roll(Calendar.DATE, -1);// The date is rolled back to the last day
        return a.get(Calendar.DATE);
    }

    /** * Determine whether the current time is between two times (range not included) **@paramStartTime startTime (format yyyyMMdd24HHmmss) 20160229160354 *@paramEndTime endTime (format yyyyMMdd24HHmmss) 20160229160354 *@return
     * @Auth NZF 2016-02-29
     */
    public static boolean isBetweenTwoTimes(String startTime, String endTime) {
        // The current time
        long nowTime = Long.parseLong(DateFormatUtils.format(new Date(), "yyyyMMddHHmmss"));
        if (StringUtils.isBlank(startTime) || StringUtils.isBlank(endTime)) {
            return false;
        }
        if (Long.parseLong(startTime) < nowTime && nowTime < Long.parseLong(endTime)) {
            return true;
        }
        return false;
    }
    /** * the 17-bit format determines whether the current time is between two times@paramStartTime startTime (format yyyyMMddHHmmssS) 20160724111924116 *@paramEndTime endTime (format yyyyMMdd24HHmmss) 20160724111924116 *@return
     * @Auth NZF 2016-02-29
     */
    public static boolean isBetweenTwoTimes17(String startTime, String endTime) {
        // The current time
        long nowTime = Long.parseLong(DateFormatUtils.format(new Date(), "yyyyMMddHHmmssS"));
        if (StringUtils.isBlank(startTime) || StringUtils.isBlank(endTime)) {
            return false;
        }
        if (Long.parseLong(startTime) < nowTime && nowTime < Long.parseLong(endTime)) {
            return true;
        }
        return false;
    }

    /* * Set the valid time to 00:05:00 */
    public static long getTodayExpireTime(a) {
        Calendar calendar = Calendar.getInstance();
        String date = DateTimeUtils.getTodayChar8();
        calendar.set(Calendar.YEAR, Integer.parseInt(date.substring(0.4)));
        calendar.set(Calendar.MONTH, Integer.parseInt(date.substring(4.6)) - 1);
        calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(date.substring(6.8)) + 1);
        calendar.set(Calendar.HOUR_OF_DAY, 00);
        calendar.set(Calendar.MINUTE, 05);
        calendar.set(Calendar.SECOND, 00);

        Calendar ccalendar = Calendar.getInstance();// The current time
        ccalendar.setTime(new Date());
        return calendar.getTimeInMillis() - ccalendar.getTimeInMillis();
    }

    /** * will expire at the end of the month **@return* /
    public static long getMonthExpireTime(a) {
        Calendar calendar = Calendar.getInstance();
        String date = DateTimeUtils.getTodayChar8();
        calendar.set(Calendar.YEAR, Integer.parseInt(date.substring(0.4)));
        calendar.set(Calendar.MONTH, Integer.parseInt(date.substring(4.6)) - 1);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);

        Calendar ccalendar = Calendar.getInstance();// The current time
        ccalendar.setTime(new Date());
        return calendar.getTimeInMillis() - ccalendar.getTimeInMillis();
    }

    /** * permanently valid (method implementation uses 1 year valid duration) **@return* /
    public static long getOneYearExpireTime(a) {
        Calendar calendar = Calendar.getInstance();
        String date = DateTimeUtils.getTodayChar8();
        calendar.set(Calendar.YEAR, Integer.parseInt(date.substring(0.4)) + 1);
        calendar.set(Calendar.MONTH, Integer.parseInt(date.substring(4.6)) - 1);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        calendar.set(Calendar.HOUR_OF_DAY, 00);
        calendar.set(Calendar.MINUTE, 00);
        calendar.set(Calendar.SECOND, 00);

        Calendar ccalendar = Calendar.getInstance();// The current time
        ccalendar.setTime(new Date());
        return calendar.getTimeInMillis() - ccalendar.getTimeInMillis();
    }

    public static Date formatDate(String date){
        Date result = null;
        fp : for (String formatPattern : dateFormatPattern) {
            try {
                simpleDateFormat.applyPattern(formatPattern);
                result = simpleDateFormat.parse(date);
                if(result ! =null) {
                    breakfp; }}catch (ParseException e) {
                // e.printStackTrace();
                //logger.info(date + " format fail");}}returnresult; }}Copy the code

Validation method

    @org.junit.Test
    public void test3(a){
        System.out.println("Unique serial number generated by means of the time tool:"+ OrderNoUtils.createOrderNo("13012345678"));
    }
Copy the code

The running results are as follows: