How to keep the order number unique

Time:2021-10-19

prologue

In the current highly concurrent distributed environment, generating unique identification is the top priority. Of course, although it is important, there is no solution. At present, there are many algorithms in the industry.
The more famous is the snowflake algorithm!!!

Algorithm source code

/**
 *Snowflake has the following structure (each part is separated by): < br >
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 
 *1-bit identification. Since the basic type of long is signed in Java, the highest bit is the sign bit, the positive number is 0 and the negative number is 1, the ID is generally a positive number and the highest bit is 0
 *41 bit timestamp (in milliseconds). Note that the 41 bit timestamp does not store the timestamp of the current time, but stores the difference of the timestamp (current timestamp - start timestamp)
 *The start time stamp here is generally the time when our ID generator starts to use, which is specified by our program (the starttime attribute of idworker class in the following program).
 *41 bit timestamp can be used for 69 years. Year t = (1L < < 41) / (1000L * 60 * 60 * 24 * 365) = 69
 *The 10 bit data machine can be deployed in 1024 nodes, including 5-bit datacenterid and 5-bit workerid
 *12 bit sequence, counting within milliseconds, and 12 bit counting sequence number. Each node can generate 4096 ID sequence numbers per millisecond (the same machine, the same timestamp)
 *Add up to just 64 bits, which is a long type.
 *The advantage of snowflake is that it is sorted by time, and there is no ID collision in the whole distributed system (distinguished by data center ID and machine ID), and the efficiency is high. After testing, snowflake can generate about 260000 IDS per second.
 * @author guoxiangwen
 */
public class SnowFlake {
    /**
     *Starting timestamp
     */
    private final static long START_STMP = 1480166465631L;

    /**
     *Number of bits occupied by each part
     */
    private final static long SEQUENCE_ BIT = 12; // Number of digits occupied by serial number
    private final static long MACHINE_ BIT = 5;   // Number of digits occupied by machine identification
    private final static long DATACENTER_ BIT = 5;// Number of bits occupied by data center

    /**
     *Maximum value of each part
     */
    //The maximum supported data ID is 31
    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
    //The maximum machine ID supported is 31 (this shift algorithm can quickly calculate the maximum decimal number represented by several binary numbers)
    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
    //The mask of the generated sequence is 4095 (0b111111 = 0xfff = 4095)
    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

    /**
     *Displacement of each part to the left
     */
    //The machine ID shifts 12 bits to the left
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    //The data ID shifts 17 bits to the left (12 + 5)
    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    //Shift the time cut to the left by 22 bits (5 + 5 + 12)
    private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

    private long datacenterId;  // Data center
    private long machineId;     // Machine identification
    private long sequence = 0L; // serial number
    private long lastStmp = -1L;// Last timestamp

    /**
     *Constructor
     *@ param datacenterid data ID (0-31)
     *@ param machineid // machine ID (0-31)
     */
    public SnowFlake(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    /**
     *Generate next ID
     *
     * @return
     */
    public synchronized long nextId() {
        //Get current timestamp
        long currStmp = getNewstmp();

        //If the current time is less than the timestamp generated by the last ID, it indicates that the system clock has fallback, and an exception should be thrown at this time
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        //If it is generated at the same time, the sequence is incremented in milliseconds
        if (currStmp == lastStmp) {
            //Within the same milliseconds, the serial number increases automatically
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //The number of sequences in the same millisecond has reached the maximum
            if (sequence == 0L) {
                currStmp = getNextMill();
            }
        } else {
            //Within different milliseconds, the serial number is set to 0
            sequence = 0L;
        }

        lastStmp = currStmp;

        return (currStmp - START_STMP) << TIMESTMP_ Left // timestamp
                | datacenterId << DATACENTER_ Left // data center
                | machineId << MACHINE_ Left // machine identification
                | sequence;                             // Serial number part
    }

    private long getNextMill() {
        long mill = getNewstmp();
        while (mill <= lastStmp) {
            mill = getNewstmp();
        }
        return mill;
    }

    private long getNewstmp() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        SnowFlake snowFlake = new SnowFlake(2, 5);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            System.out.println(snowFlake.nextId());
        }
        System. Out. Println ((system. Currenttimemillis() - start) / 1000 + "seconds");
    }
}

problem

The code is simple, but there are some problems when using distributed systems:

1. How do different servers use different workids and datacenter IDS?

2. This class is set to single instance initialization?

The solution is as follows:

1. When the micro service is started, workid and datacenter ID are passed in as parameters

2. Use the Component annotation to set snowflakeidworker class as singleton initialization

The specific codes are as follows:

package com.king.videoproject.config;

import com.king.videoproject.util.SnowFlake;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class SnowFlakeCompone {
    @Value("${server.workId}")
    private long workId;

    @Value("${server.datacenterId}")
    private long datacenterId;

    private static volatile SnowFlake instance;

    /**
     *Get instance
     * @return
     */
    public SnowFlake getInstance(){
        if(instance == null){
            synchronized (SnowFlake.class){
                if(instance == null){
                    instance = new SnowFlake(workId, datacenterId);
                }
            }
        }
        return instance;
    }
}

How to call?
Please look

package com.king.videoproject.controller;

import com.king.videoproject.config.SnowFlakeCompone;
import com.king.videoproject.util.SnowFlake;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    SnowFlakeCompone snowFlakeCompone;

    @RequestMapping("/thread")
    public Map<String, Object> testThread(){
        Set<Long> set = new HashSet<>();
        for(int i = 0;i < 100000;i++){
            System. Out. Println ("thread Name:" + thread. Currentthread(). Getid() + "ID:" + snowflakecomposite. Getinstance(). Nextid());
            set.add(snowFlakeCompone.getInstance().nextId());
        }
        System. Out. Println ("length" + set. Size());
        Map<String, Object> result = new HashMap<String, Object>();
        return result;
    }

}

The above uses springboot, and the corresponding configuration file bootstrap.yml is configured as follows(Warning: it is better to use bootstrap.yml instead of application.yml because the priority is high to prevent overwriting or failure to take effect

#Development environment configuration
server:
  #Service port
  port: 8888
  #Service address
  address: localhost

  workId: 2
  datacenterId: 5

After this configuration, you can pass the postman test!!!

Draw lessons from https://www.cnblogs.com/cs99l…