When the traditional monomer architecture, we are basically a single library and then the business single table structure. Generally, the ID of each service table is incremented from 1, and the AUTO_INCREMENT=1 is used to set the starting value of self-increment. However, in the distributed service architecture mode, multiple databases or tables are designed to store the same service data. In this case, the same ID will be generated according to the increment ID of the database, and the uniqueness of the primary key cannot be guaranteed.

As shown in the figure above, the order ID is 1 if the first order is stored on DB1, and the order ID is 1 when a new order is stored on DB2. Although the architecture of our system is distributed, it should be insensitive at the user level, and duplicate order primary keys are obviously not allowed. So how do you make primary key unique for distributed systems?

UUID

Universally Unique Identifier (UUID). A UUID is a set of 32-bit hexadecimal numbers, so the theoretical total number of UUID’s is 16^32=2^128, which is approximately 3.4 x 10^38. So if you generate 1 trillion UUID per nanosecond, it would take 10 billion years to use up all of them.

The generated UUID consists of data in the format of 8-4-4-12, consisting of 32 characters and four hyphens (‘ – ‘). UUID.toString().replaceAll(“-“,””) is normally deleted.

Currently, there are five versions of UUID generation methods. Each version has different algorithms and application scopes.

  • Time-based UUID – Version 1: This is generally through the current time, random Numbers, and local Mac address to calculate, by org. Apache. Logging. Log4j. Core. The util package UuidUtil. GetTimeBasedUuid () to use or any other tool in the package. The use of MAC addresses ensures uniqueness, but at the same time exposes MAC addresses, which is not private enough.

  • DCE secure UUID – Version 2 The Distributed Computing Environment (DCE) secure UUID is the same as the time-based UUID algorithm, but the first four positions of the timestamp are changed to POSIX Uids or Gids. This version of UUID is rarely used in practice.

  • Name-based UUID (MD5) – Version 3 name-based UUID is worth it by calculating the MD5 hash of the name and namespace. This version of UUID guarantees the uniqueness of UUID generated by different names in the same namespace; Uniqueness of UUID in different namespaces; UUID duplication in the same namespace with the same name is the same.

  • Random UUID – Version 4 generates UUID based on random, or pseudo-random, numbers. The probability of such UUID duplicates can be calculated, but the probability of duplicates is negligible, so this version is also the one that is often used. This is the version used in the JDK.

  • Name-based UUID (SHA1) – Version 5 is similar to the name-based UUID Algorithm except that the Secure Hash Algorithm 1 (SHA1) Algorithm is used to calculate the Hash value.

Our Java JDK UUID generation method is version 4 based on random number generated UUID and version 3 based on name UUID, interested can go to see its source code.

public static void main(String[] args) {

    // Get a version 4 UUID based on a random byte array.
    UUID uuid = UUID.randomUUID();
    System.out.println(uuid.toString().replaceAll("-".""));

    // Get a version 3(based on name) UUID based on the specified byte array.
    byte[] nbyte = {10.20.30};
    UUID uuidFromBytes = UUID.nameUUIDFromBytes(nbyte);
    System.out.println(uuidFromBytes.toString().replaceAll("-".""));
}
Copy the code

The resulting UUID result,

59f51e7ea5ca453bbfaf2c1579f09f1d
7f49b84d0bbc38e9a493718013baace6
Copy the code

Although UUID generation is easy and local generation has no network overhead, there are some disadvantages to using UUID,

  • Not easy to store: UUID is too long, 16 bytes 128 bits, usually represented as a string of 36 lengths, which is not suitable for many scenarios.
  • Information insecurity: The algorithm that generates UUID based on MAC address may cause MAC address leakage and reveal the user’s location.
  • Bad for MySQL index: If used as the primary key of database, in InnoDB engine, the disorder of UUID may cause the data location to change frequently, which seriously affects performance. Please refer to MySQL index principle B+ tree for knowledge.

Database generation

Is it necessary to meet the requirements of distributed unique IDS based on external conditions? Can we get the ids we need on the basis of our distributed database?

Since the initial self-increment of distributed database is the same, there will be conflicts. Therefore, we design the self-increment ID of the same business table in the database of distributed system into a different initial value, and then set a fixed step length, which is the number of sub-databases or sub-tables.

MySQL, for example, sets auto_increment_increment and auto_increment_offset to ensure that the ID is incremented.

  • Auto_increment_offset: indicates the number from which the increment field starts. Its value range is 1.. 65535.
  • Auto_increment_increment: indicates the increment amount of the self-growing field each time. The default value is 1 and the value range is 1.. 65535.

Suppose there are three machines, then the starting ID value of the ORDER table in DB1 is 1, the starting ID value of the ORDER table in DB2 is 2, and the starting ID value of the ORDER table in DB3 is 3, and their increment step size is 3, then their ID generation range is as follows:

In this way, the obvious advantage is to rely on the database itself does not need other resources, and the ID number monotonously increases, you can achieve some services with special requirements on ID.

But the disadvantages are also very obvious, first of all, it is strongly dependent on DB, when DB is abnormal when the whole system is unavailable. Although configuring master/slave replication can increase availability as much as possible, data consistency cannot be guaranteed in special cases. Inconsistencies during primary/secondary switchover may cause repeated numbers. In addition, the ID sending performance bottleneck is limited to the read and write performance of a single MySQL server.

Implemented using Redis

Redis implements distributed unique ids mainly by providing autoatomic commands such as INCR and INCRBY, which guarantee that the generated ids will be uniquely ordered due to the single-threaded nature of Redis.

However, a single machine has a performance bottleneck and cannot meet the requirements of high-concurrency services. Therefore, a cluster can be used to implement the system. The clustering approach involves the same problems as database clustering, so you need to set segmentation and step sizes to achieve this.

In order to avoid large numbers after long-term increment, it can be combined with the current timestamp. In addition, in order to ensure concurrency and business multithreading, Redis + Lua can be used to code, to ensure safety.

Redis implements distributed global unique ID, and its performance is relatively high. The generated data is in order, which is beneficial to sorting services. However, it also relies on Redis and requires the system to introduce Redis components, increasing the configuration complexity of the system.

Of course, Redis is now common, so if other businesses have already introduced Redis clustering, then resource utilization can be considered using Redis for implementation.

Snowflake algorithm -Snowflake

Snowflake is an open-source distributed ID generation algorithm created by Twitter that divides 64-bit bits into multiple parts in a namespace, each part representing a different meaning. In Java, 64-bit integers are of type Long, so in Java, the ids generated by SnowFlake are stored by longs.

  • The first bit occupies 1bit, and its value is always 0. It can be regarded as a sign bit.
  • The 41 bits starting from the second bit are the timestamp, and the 41-bit bits can represent 2^41 numbers, each of which represents milliseconds, so the available time life of the snowflake algorithm is (1L<<41)/(1000L360024*365)=69 years.
  • The 10-bit in the middle can represent the number of machines, that is, 2^10 = 1024 machines, but we would not normally deploy such machines. If we need IDC (Internet data center), we can divide 10-bit into 5-bit for IDC and 5-bit into working machine. In this case, 32 IDCs can be represented. Each IDC can contain 32 machines. The division can be defined based on your requirements.
  • The last 12-bit is an increment sequence that can represent the number 2^12 = 4096.

This partitioning is equivalent to producing 4096 ordered non-repeating ids on a machine in a data center in a millisecond. But we definitely have more than one IDC and more than one machine, so we double the number of ordered ids that can be generated in milliseconds.

The official version of Snowflake’s Twitter is written in Scala. For those of you who have studied Scala, here is the Java version.

package com.jajian.demo.distribute;

/** * Twitter_Snowflake

* The structure of SnowFlake is as follows (each part is separated by -):

* 0-0000000000 0000000000 0000000000 0000000000 0-00000 - 0000000-000000000000

* 1 bit identifier, because the basic type of long is signed in Java, the highest bit is the sign bit, the positive number is 0, the negative number is 1, so the id is generally positive, the highest bit is 0

* 41 bit truncation (milliseconds), notice, The 41-bit cut-off is not the current cut-off, but the difference between the current cut-off and the start cut-off *. The start cut-off is usually the time our ID generator started using. Specified by our program (as shown in the startTime property of the IdWorker class). The 41-bit time segment can be used for 69 years. The year T = (1L << 41)/(1000L * 60 * 60 * 24 * 365) = 69< BR > * the 10-bit data machine bit can be deployed on 1024 nodes. Includes 5-bit datacenterId and 5-bit workerId< BR > * 12-bit sequence, count in milliseconds, 12-bit count sequence number support each node every millisecond (same machine, same time intercept) 4096 ID sequence number < BR > * add up to just 64 bits, a Long. < BR > * The advantage of SnowFlake is that the whole system is sorted by time increment, there is no ID collision across the distributed system (distinguished by data center ids and machine ids), and it is very efficient. SnowFlake can generate around 260,000 ids per second in tests. * /
public class SnowflakeDistributeId { // ==============================Fields=========================================== /** * Start time (2015-01-01) */ private final long twepoch = 1420041600000L; /** * The number of bits occupied by 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 largest 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; /** * Move the machine ID 12 bits to the left */ private final long workerIdShift = sequenceBits; /** * Data id is moved 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, where 4095 (0b111111111111= 0xFFf =4095) */ private final long sequenceMask = -1L ^ (-1L << sequenceBits); /** * Working machine ID(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 SnowflakeDistributeId(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) { returnSystem.currentTimeMillis(); }}Copy the code

The code for the test is as follows

public static void main(String[] args) {
    SnowflakeDistributeId idWorker = new SnowflakeDistributeId(0.0);
    for (int i = 0; i < 1000; i++) {
        long id = idWorker.nextId();
// System.out.println(Long.toBinaryString(id));System.out.println(id); }}Copy the code

Snowflake algorithm provides a good design idea. The ID generated by snowflake algorithm increases in trend and does not rely on third-party systems such as databases. It is deployed as a service with higher stability and very high performance in ID generation.

However, the Snowflake algorithm relies heavily on the machine clock. If the clock is dialed back on the machine, the number will be repeated or the service will be unavailable. If some ids happened to be generated before the rollback and the time went back, the generated ids might be duplicated. There is no official solution for this, but a simple throw error will cause the service to be unavailable until the time is recovered.

Many other snowflake algorithms are also designed on this idea and improved to avoid its defects. The Snowflake mode in Baidu UidGenerator and Meituan distributed ID generation system Leaf introduced later is evolved on the basis of Snowflake.

Baidu – UidGenerator

Baidu UidGenerator is baidu’s open source Java language based unique ID generator, which is an improvement on the snowflake algorithm. The UidGenerator works in application projects as a component and supports user-defined workerId bits and initialization policies. It is applicable to scenarios where instances automatically restart or drift in a hypervisor such as Docker.

In terms of implementation, UidGenerator provides two methods for generating unique ids, DefaultUidGenerator and CachedUidGenerator. It is recommended to use CachedUidGenerator if performance is concerned.

UidGenerator still divides 64-bit bits into namespaces, but it does this differently from snowflake by default. It defaults to the 1-28-22-13 format. You can adjust the number of digits occupied by each field according to your business situation and characteristics.

  • The first bit still takes up 1bit, and its value is always 0.
  • The 28 bits beginning with the second bit are time stamps. The 28-bit bits can represent 2^28 numbers.It’s not in milliseconds, it’s in seconds, each number representing seconds is available (1L<<28)/(360024365) ≈ 8.51 years.
  • The intermediate workId (data center + work machine, can be composed of other ways) is composed of 22-bit, can represent 2^22 = 4194304 workId.
  • Finally, it consists of 13-bit self-increasing sequence, which can represent the number of 2^13 = 8192.

WorkId (machine id) supports a maximum of 420w machine startup times. The built-in implementation is allocated by the database at startup (table name WORKER_NODE). The default allocation policy is deprecated and reuse policy can be provided later.

DROP TABLE IF EXISTS WORKER_NODE;
CREATE TABLE WORKER_NODE
(
ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id',
HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name',
PORT VARCHAR(64) NOT NULL COMMENT 'port'.TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER',
LAUNCH_DATE DATE NOT NULL COMMENT 'launch date',
MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time',
CREATED TIMESTAMP NOT NULL COMMENT 'created time',
PRIMARY KEY(ID))COMMENT='DB WorkerID Assigner for UID Generator'.ENGINE = INNODB;
Copy the code

DefaultUidGenerator implementation

DefaultUidGenerator is a normal generator based on the timestamp and machine bit and serial number, similar to the Snowflake algorithm, and only throws exceptions for clock callback. There are only a few differences, such as being measured in seconds instead of milliseconds and supporting virtualization environments such as Docker.

protected synchronized long nextId(a) {
    long currentSecond = getCurrentSecond();

    // Clock moved backwards, refuse to generate uid
    if (currentSecond < lastSecond) {
        long refusedSeconds = lastSecond - currentSecond;
        throw new UidGenerateException("Clock moved backwards. Refusing for %d seconds", refusedSeconds);
    }

    // At the same second, increase sequence
    if (currentSecond == lastSecond) {
        sequence = (sequence + 1) & bitsAllocator.getMaxSequence();
        // Exceed the max sequence, we wait the next second to generate uid
        if (sequence == 0) {
            currentSecond = getNextSecond(lastSecond);
        }

    // At the different second, sequence restart from zero
    } else {
        sequence = 0L;
    }

    lastSecond = currentSecond;

    // Allocate bits for UID
    return bitsAllocator.allocate(currentSecond - epochSeconds, workerId, sequence);
}
Copy the code

If you want to implement DefaultUidGenerator, the above number of bits can be configured via Spring.

<bean id="defaultUidGenerator" class="com.baidu.fsg.uid.impl.DefaultUidGenerator" lazy-init="false">
    <property name="workerIdAssigner" ref="disposableWorkerIdAssigner"/>

    <! -- Specified bits & epoch as your demand. No specified the default value will be used -->
    <property name="timeBits" value="29"/>
    <property name="workerBits" value="21"/>
    <property name="seqBits" value="13"/>
    <property name="epochStr" value="2016-09-20"/>
</bean>
Copy the code

CachedUidGenerator implementation

The official recommendation for a high-performance CachedUidGenerator is to use the RingBuffer cache to generate ids. Each element of the array becomes a slot. RingBuffer capacity. The default value is the maximum value (2^13 = 8192) of the Snowflake algorithm. You can configure boostPower to expand capacity to improve RingBuffer read/write throughput.

The Tail Cursor is used to read and write slots on a circular array:

  • The Tail pointer represents the maximum sequence number (starting from 0 and increasing continuously) produced by Producer. Tail cannot exceed Cursor, that is, producers cannot overwrite unconsumed slots. When Tail has caught up with curosr, you can specify a PutRejectPolicy with rejectedPutBufferHandler

  • The Cursor pointer represents the minimum sequence number that a Consumer consumes (the sequence is the same as the Producer sequence). The Cursor cannot exceed Tail, that is, it cannot consume unproduced slots. When the Cursor is catch up with the tail, through rejectedTakeBufferHandler TakeRejectPolicy specified at this time

The CachedUidGenerator uses two ringBuffers, uID-RingBuffer for storing UUIds and flag-ringBuffer for storing Uid states (fillable and consumable).

Because array elements are allocated continuously in memory, the CPU cache can be maximized to improve performance. But at the same time, FalseSharing problem will be brought, so the method of CacheLine complement is adopted in Tail, Cursor pointer and flag-ring buffer.

RingBuffer filling time

  • Initializing prefilled RingBuffer During initialization, the entire RingBuffer is prefilled.

  • When filling the Take consumption, check the number of available slots immediately. If the number of available slots is smaller than the preset threshold, complete the slots. The threshold can be configured using paddingFactor, as described in Quick Start for CachedUidGenerator configuration.

  • Periodic filling is done by the Schedule thread, and periodic completion is done by the vacant slots. ScheduleInterval can be configured to apply the scheduling function and specify Schedule intervals.

Meituan Leaf

Leaf is a distributed ID generation service launched by Meituan Basic RESEARCH and development platform. Its name is derived from the famous phrase of German philosopher and mathematician Leibniz: “There are no two identical leaves in the world”.

Leaf also provides two ways of ID generation, namely, leaf-segment database scheme and Leaf-Snowflake scheme.

Leaf-segment database scheme

The leaf-Segment database scheme is based on the database scheme described above, which makes the following changes:

  • The original scheme has to read and write the database every time to obtain the ID, causing the database pressure. Use proxy server to obtain the value of one segment at a time. After use, go to the database to obtain a new number segment, which can greatly reduce the pressure of the database.

  • The biz_tag field is used to distinguish the number issuing requirements of each service. The biz-tag IDS are isolated from each other and do not affect each other. If you need to expand the database to meet performance requirements, you do not need to perform the complex capacity expansion operations described above. You only need to divide the biz_TAG database into different tables.

Database table design is as follows:

CREATE TABLE `leaf_alloc` (
  `biz_tag` varchar(128)  NOT NULL DEFAULT ' ' COMMENT 'the key of business'.`max_id` bigint(20) NOT NULL DEFAULT '1' COMMENT 'Maximum ID currently assigned'.`step` int(11) NOT NULL COMMENT 'Initial step size, also dynamically adjusted minimum step size'.`description` varchar(256)  DEFAULT NULL COMMENT 'Description of business Key'.`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time',
  PRIMARY KEY (`biz_tag`))ENGINE=InnoDB;
Copy the code

Instead of writing to the database every time you get an ID, you just need to set step to a large enough size, such as 1000. The database will be read and written again only after 1000 numbers have been consumed. The frequency of reading and writing to the database decreases from 1 to 1/step, as shown below:

At the same time, in order to solve the problem of TP999 (the minimum time required to meet 99/1000 network requests) data fluctuation, leaf-segment will still hang on updating database I/O after the number segment is used up, TP999 data will occasionally appear spikes, providing double buffer optimization.

To put it simply, the timing of retrieving the number segment from Leaf is carried out when the number segment is exhausted, which means that the ID delivery time of the critical point of the number segment depends on the time of retrieving the number segment from DB next time, and the incoming request during this period will also cause the thread to block because the DB number segment is not retrieved. If the network and DB performance is stable, this situation has little impact on the system, but if the network jitter occurs when the DB is taken, or the DB slow query will lead to the response time of the whole system is slow.

In order to make the process of DB number segment non-blocking, there is no need to block the request thread when THE DB number segment, that is, when the number segment consumption reaches a certain point, the next number segment is asynchronously loaded into the memory, and there is no need to wait until the number segment is exhausted to update the number segment. This can greatly reduce the TP999 index of the system. Detailed implementation is shown in the figure below:

In the form of double buffer, Leaf service has two number cache segments inside. When 10% of the current number segment is delivered, if the next number segment is not updated, another update thread is started to update the next number segment. After the current segment is delivered, if the next segment is ready, the system switches to the next segment and delivers the current segment.

  • Each Biz-Tag has consumption speed monitoring, and it is generally recommended to set segment length to 600 times of QPS issued during peak service periods (10 minutes), so that Leaf can continue to issue numbers for 10-20 minutes without being affected even if DB goes down.

  • Each request determines the status of the next number segment and updates the next number segment. Therefore, occasional network jitter does not affect the update of the next number segment.

There are still some problems with this scheme. It still depends on the stability of DB, so it needs to adopt the master-slave backup method to improve the availability of DB. Besides, the ID generated by leaf-segment scheme is trend increasing, so that the ID number can be calculated, such as the order ID generation scenario. By subtracting order ID numbers, you can roughly calculate the order volume of the company in a day, which is unbearable.

The Leaf – snowflake

Leaf-snowflake completely adopts the bit design of Snowflake. The allocation of workerID introduces the feature of Zookeeper persistent sequence node to automatically configure wokerID for Snowflake node. It avoids the problem of high cost of manual configuration when the service scale is large.

Leaf-snowflake starts with the following steps:

  • Start the Leaf-Snowflake service, connect to Zookeeper, and check whether you are registered under the leaf_forever parent node (if there are children in this order).
  • If you have registered your workerID (int ID generated by the zK sequence node), start the service.
  • If not, create a persistent sequence node under the parent node, retrieve the sequence number as its own workerID, and start the service.

To reduce the dependency on Zookeeper, a workerID file is cached on the native file system. When ZooKeeper is faulty and the ZooKeeper needs to be restarted, the ZooKeeper service can be started properly.

As mentioned above, there is a clock rollback problem in the snowflake algorithm. Leaf-snowflake solves the clock rollback problem by checking its system time and comparing it with the time recorded by leaf_forever/${self} node and then initiating an alarm.

The OFFICIAL advice of THE United States is to disable NTP synchronization directly because the system is highly dependent on the clock and is sensitive to time requirements. NTP synchronization may cause a second-level rollback when the system is running. Otherwise, you can directly return to ERROR_CODE without providing the service during clock rollback and wait for the clock to catch up. Or do a layer of retry, and then report to the alarm system, or detect a clock back after the automatic removal of its node and alarm.

In terms of performance, the official data provided by the current Leaf performance in 4C8G machine QPS can pressure measured to nearly 5W /s, TP999 1ms.

conclusion

The above basically lists all the commonly used distributed ID generation methods. In fact, the general classification can be divided into two categories:

One is db-like, according to set different start value and step size to achieve trend increasing, need to consider the fault tolerance and availability of the service.

The other is the Snowflake type, which splits the 64-bit bits into different segments, each with a different meaning, basically a timestamp, a machine ID, and a sequence number. This solution is to consider the problem of clock callback and do some buffer design to improve performance.

Moreover, the lifetime and concurrency can be changed by dividing the three (timestamp, machine ID, sequence number) into different bits.

For example, for applications that require less concurrency and expect long-term use, you can increase the number of timestamp bits and reduce the number of sequence bits. For example, if {“workerBits”:23,”timeBits”:31,”seqBits”:9} is configured, 28 nodes can run for 68 years at a concurrent capacity of 14400 UID/s.

For applications with frequent node restart and expected long-term use, the number of working machine bits and timestamp bits can be increased while the number of sequence bits can be reduced. For example, if {“workerBits”:27,”timeBits”:30,”seqBits”:6} is set to {“workerBits”:27,”timeBits”:30,”seqBits”:6}, 37 nodes can run for 34 years at the total concurrency of 2400 UID/s.

Reference:

  1. blog.csdn.net
  2. Github.com/baidu/uid-g…
  3. tech.meituan.com
  4. mp.weixin.qq.com