[development experience] principle of automatic generation of machine id by mybatis plus snowflake algorithm

Posted by sheephat on Tue, 01 Feb 2022 18:14:21 +0100

1. Principle of snowflake algorithm

The snowflake algorithm uses a 64 bit long number as the globally unique id. Among the 64 bits, one bit is not used, and then 41 bit is used as the number of milliseconds, 10 bit as the working machine id and 12 bit as the serial number.

  1. 1 bit, no, because the highest bit in the binary is the sign bit, 1 represents a negative number and 0 represents a positive number. Generally, the highest order id is 0.
  2. 41bit timestamp, which is used to record the timestamp in milliseconds.
  3. 10bit - working machine id, which is used to record the working machine id.
  4. 12bit serial number, which is used to record different IDs generated in the same millisecond. That is, 4095 numbers of 0, 1, 2, 3,... 4094 can be used to represent 4095 ID serial numbers generated in the same time section (milliseconds) of the same machine.

SnowFlake guarantees:

  1. All generated IDs are incremented according to the time trend
  2. There will be no duplicate IDS in the whole distributed system (because there are datacenterId and workerId to distinguish)

As above, I have roughly understood the principle of snowflake algorithm and the importance of machine number to snowflake algorithm. If the machine number is the same, the id may be repeated.

Since 3.3.0, mybatis plus has used snowflake algorithm + UUID (excluding middle dash) by default, but it does not force developers to configure machine numbers. This is very confused, which may make people who do not understand the snowflake algorithm bury a hole.
But is such a powerful framework really not optimized? With questions, check out the source code of mybatis plus snowflake algorithm. Com baomidou. mybatisplus. core. toolkit. Sequence. Finally, it is found that when the machine number is not set, it will be automatically generated through the current physical network card address and jvm process ID. This is really a better solution. Generally, in a cluster, the probability of MAC+JVM process being the same as PID is very small.

2. Automatic generation of unique machine number source code

Core code. There are two construction methods, one nonparametric construction and one parametric construction.

public Sequence() {
    //Get datacenter ID through the current physical network card address
    this.datacenterId = getDatacenterId(maxDatacenterId);
    //Get workerId from physical network card address + jvm process pi
    this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
}

/**
 * Parametric constructor
 *
 * @param workerId     Work machine ID
 * @param datacenterId serial number
 */
public Sequence(long workerId, long datacenterId) {
    Assert.isFalse(workerId > maxWorkerId || workerId < 0,
            String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
    Assert.isFalse(datacenterId > maxDatacenterId || datacenterId < 0,
            String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
    this.workerId = workerId;
    this.datacenterId = datacenterId;
}
  • When the parameterless construction developer does not set the machine number
  • The machine number is set by the developer of the participating structure
protected static long getDatacenterId(long maxDatacenterId) {
    long id = 0L;
    try {
        //Get the local (or server ip address)
        //DESKTOP-123SDAD/192.168.1.87
        InetAddress ip = InetAddress.getLocalHost();
        NetworkInterface network = NetworkInterface.getByInetAddress(ip);
        //Generally, it is not null and will enter else
        if (network == null) {
            id = 1L;
        } else {
            //Get physical network card address
            byte[] mac = network.getHardwareAddress();
            if (null != mac) {
                id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
                id = id % (maxDatacenterId + 1);
            }
        }
    } catch (Exception e) {
        logger.warn(" getDatacenterId: " + e.getMessage());
    }
    return id;
}
/**
 * Get maxWorkerId
 */
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
    StringBuilder mpid = new StringBuilder();
    mpid.append(datacenterId);
    //Get jvm process information
    String name = ManagementFactory.getRuntimeMXBean().getName();
    if (StringUtils.isNotBlank(name)) {
        /*
         * Get process PID
         */
        mpid.append(name.split(StringPool.AT)[0]);
    }
    /*
     * MAC + PID The hashcode of gets 16 low bits
     */
    return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}

Topics: Java MybatisPlus