Global Unique ID Design Scheme

In a distributed system, it is often necessary to use a globally unique ID to find the corresponding data. Generating such IDs requires ensuring that the system is globally unique, and requires high performance and takes up relatively little space.

The global unique ID is generally set as the primary key in the database, so in order to ensure the rapid establishment of the index when data is inserted, it is necessary to maintain an orderly trend.

In this way, the global unique ID needs to ensure these two requirements:
global uniqueness, orderly
trend
, several ways to generate global ID, and


database auto-increment
. When the database used by the service only has a single database and a single table, you can use the auto_increment of the database to generate a global unique incremental ID. .Advantages

:
Simple, no additional operations required by the program
Maintain a fixed-length increment
Uniqueness in a single table

Disadvantages :
Poor performance under high concurrency, the upper limit of the performance generated by the primary key is the upper limit of the single database server.
Horizontal expansion is difficult, and in a distributed database environment, uniqueness cannot be guaranteed.

UUID
The general language will have its own implementation of UUID, such as the UUID method UUID.randomUUID().toString() in Java, which can be generated locally by the service program, and the generation of ID does not depend on the implementation of the database.

Advantages:
IDs are generated locally, no remote calls are required.
Globally unique and unique.
The horizontal scalability is very good.

Disadvantages:
The ID has 128 bits, which takes up a lot of space and needs to be stored as a string type, and the indexing efficiency is extremely low.
There is no Timestamp in the generated ID, and the trend cannot be guaranteed to increase.


Twitter Snowflake

snowflake is an open source distributed ID generation algorithm of twitter. Its core idea is to generate a long ID, using 41 bits as the number of milliseconds, 10 bits as the machine number, and 12 bits as the serial number within milliseconds. This algorithm can theoretically generate up to 1000*(2^12) IDs per second, which is about 400W of IDs, which can fully meet the needs of the business.

According to the idea of ​​snowflake algorithm, we can generate our own globally unique ID according to our own business scenarios. Because the length of the long type in Java is 64bits, the ID we design needs to be controlled within 64bits.

For example, the ID we designed contains the following information:
| 41 bits: Timestamp | 3 bits: region | 10 bits: machine number | 10 bits: serial number |

Java code to generate unique ID: modified

package com.pandy.framework.core.id;

import java.security.SecureRandom;

/**
 * Created by pandy on 16-6-27.
 * <p/>
 * Project name: workspace * Function description:
 * snowflake is an open source distributed ID generation algorithm for twitter. Its core idea is:
 * Generate a long ID, using 41bit as the number of milliseconds, 10bit as the machine number, and 12bit as the serial number in milliseconds.
 * This algorithm can theoretically generate up to 1000*(2^12) IDs per second, which is about 400W of IDs, which can fully meet the needs of the business.
 * According to the idea of ​​snowflake algorithm, we can generate our own globally unique ID according to our own business scenarios.
 * Because the length of the long type in Java is 64bits, the ID we design needs to be controlled within 64bits.
 * For example, the ID we designed contains the following information:
 * | 41 bits: Timestamp | 3 bits: region | 10 bits: machine number | 10 bits: serial number |
 * Created by: Pandy,
 * Email: [email protected], [email protected]
 * Copyright:
 * Official website:
 * Date of creation: 16-6-27.
 * Creation time: 10:59 PM.
 * Modification history:
 * -----------------------------------------------
 */

/**
 * When creating: workerId can be used to mark the serial number of the table (when the table is less than 1024 tables),
 * Or the serial number of the module (if the table is larger than 1024 tables, the serial number is defined according to the module to which the table belongs),
 * Avoid duplicate IDs
 * Custom ID generator ID generation rules: ID up to 64 bits
 *
 * | 41 bits: Timestamp (milliseconds) | 3 bits: area (machine room) | 10 bits: machine number | 10 bits: serial number |
 */
public class CustomUUID {

    // base time
    private long twepoch = 1288834974657L; // Thu, 04 Nov 2010 01:42:54 GMT
    // area flag bits
    private final static long regionIdBits = 3L;
    // Machine ID bits
    private final static long workerIdBits = 10L;
    // serial number identification digits
    private final static long sequenceBits = 10L;

    // The maximum value of the area flag ID
    private final static long maxRegionId = -1L ^ (-1L << regionIdBits);
    // Maximum machine ID
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
    // Maximum serial number ID
    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);

    // machine ID offset left by 10 bits
    private final static long workerIdShift = sequenceBits;
    // The business ID is shifted to the left by 20 bits
    private final static long regionIdShift = sequenceBits + workerIdBits;
    // time milliseconds shifted left by 23 bits
    private final static long timestampLeftShift = sequenceBits + workerIdBits + regionIdBits;

    private static long lastTimestamp = -1L;

    private long sequence = 0L;
    private final long workerId;
    private final long regionId;

    //workerId>=0 && workerId<1024
    //regionId>=0 && regionId<8
    public CustomUUID(long workerId, long regionId) {

        // Throw an exception if out of bounds
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
        }
        if (regionId > maxRegionId || regionId < 0) {
            throw new IllegalArgumentException(
                    "datacenter Id can't be greater than %d or less than 0");
        }

        this.workerId = workerId;
        this.regionId = regionId;
        System.out.println("Region ID maximum value=" + maxRegionId + ", Machine ID maximum value=" + maxWorkerId + ", Serial ID maximum value=" + sequenceMask);
    }

    public CustomUUID(long workerId) {
        // Throw an exception if out of bounds
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
        }
        this.workerId = workerId;
        this.regionId = 0;
        System.out.println("Region ID maximum value=" + maxRegionId + ", Machine ID maximum value=" + maxWorkerId + ", Serial ID maximum value=" + sequenceMask);
    }

    public long generate() {
        return this.nextId(false, 0);
    }

    /**
     * The actual generated code
     *
     * @param isPadding
     * @param busId
     * @return
     */
    private synchronized long nextId(boolean isPadding, long busId) {

        long timestamp = timeGen();
        long paddingnum = regionId;

        if (isPadding) {
            paddingnum = busId;
        }

        if (timestamp < lastTimestamp) {
            try {
                throw new Exception("Clock moved backwards.  Refusing to generate id for "
                        + (lastTimestamp - timestamp) + " milliseconds");
            } catch (Exception e) {
                e.printStackTrace ();
            }
        }

        // If the last generated time is the same as the current time, within the same millisecond
        if (lastTimestamp == timestamp) {
            // The sequence is incremented, because the sequence is only 10bit, so compare it with the sequenceMask and remove the high bits
            sequence = (sequence + 1) & sequenceMask;
            // Judge whether it overflows, that is, it exceeds 1024 in every millisecond. When it is 1024, it is compared with sequenceMask, and sequence is equal to 0
            if (sequence == 0) {
                // spin wait until next millisecond
                timestamp = tailNextMillis(lastTimestamp);
            }
        } else {
            // If it is different from the last generation time, reset the sequence, that is, the next millisecond starts, and the sequence count starts to accumulate again from 0,
            // In order to ensure more randomness of the mantissa, set a random number for the last digit
            sequence = new SecureRandom().nextInt(10);
        }

        lastTimestamp = timestamp;

        return ((timestamp - twepoch) << timestampLeftShift) | (paddingnum << regionIdShift)
                | (workerId << workerIdShift) | sequence;
    }

    // Prevent the generated time from being smaller than the previous time (due to NTP callbacks, etc.) and keep the incremental trend.
    private long tailNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    // get the current timestamp
    protected long timeGen() {
        return System.currentTimeMillis();
    }


    public static void main(String args[]) {
        CustomUUID uuid1 = new CustomUUID(1l, 0);
        CustomUUID uuid2 = new CustomUUID(2l, 0);
        for (int i = 0; i < 100; i++) {
            System.out.println(uuid1.nextId(false, 0));
            System.out.println(uuid2.nextId(false, 0) + "-");
        }

    }

}

There are a few points to note when using this custom method:

In order to maintain the growth trend, it is necessary to avoid the time of some servers being early and the time of some servers being late. It is necessary to control the time of all servers, and to avoid the NTP time server calling back to the server time.
In the span of milliseconds, the serial number always returns to 0, which will cause more IDs with serial numbers of 0, resulting in uneven ID modulo. of random numbers.
Using this CustomUUID class, it is best to keep the singleton mode running in a system.
Original address: http://www.androidchina.net/4744.html

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326939621&siteId=291194637