Overview
In distributed systems, there are scenarios that require the use of a globally unique ID, which can be used to prevent ID collisions using a 36-bit UUID, but the UUID has some drawbacks, first he is relatively long, and the UUID is generally unordered.
There are times when we want to use a simple ID and want the ID to be generated in an orderly fashion.
The snowflake of Twitter solved this demand, and initially Twitter migrated the storage system from MySQL to Cassandra, because Cassandra had no sequential ID generation mechanism, so it developed a set of global unique ID generation services.
Structure
The structure of the snowflake is as follows (each part with-separate):
0-0000000000 0000000000 0000000000 0000000000 0-00000-00000-000000000000
The first bit is unused, the next 41 bits are millisecond (41-bit lengths can be used for 69 years), then 5-bit Datacenterid and 5-bit Workerid (10-bit lengths support deployment of up to 1024 nodes), The last 12 bits are counted in milliseconds (the 12-bit count sequence number supports 4,096 ID numbers per millisecond for each node)
Altogether add up just 64 bits, for a long type. (up to 19 length after conversion to string)
The IDs generated by the snowflake are sorted on a per-time basis, and no ID collisions (differentiated by datacenter and Workerid) are generated throughout the distributed system and are highly efficient. Tested snowflake can generate 260,000 IDs per second.
Source
(Java version of source code)
The structure of/** * twitter_snowflake<br> * snowflake is as follows (each part with-separate):<br> * 0-0000000000 0000000000 0000000000 0000000000 0 -00000-00000-000000000000 <br> * 1-bit ID, because the long base type 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<br> * 41 bit Time truncation (millisecond level), note that the 41-bit time-intercept is not the time-intercept that stores the current time, but rather the difference in the time-intercept (the current time-stop-start-time-cut) * Gets the value), where the start time is usually the time that our ID generator started to use, specified by our program (the following program Idworker the StartTime property of the class below). 41-bit time-cut, can be used 69 years, Year t = (1L << x)/(1000L * x * * 365) = 69<br> * 10 bits of data machine, can be deployed on 1024 nodes, including 5-bit datacente RID and 5-bit workerid<br> * 12-bit sequence, count in milliseconds, 12-bit count sequence number supports each node per millisecond (same machine, same time intercept) to generate 4,096 ID number <br> * add up just 64 bits, for a long type. <br> * Snowflake's advantage is that the overall time-to-order, and the entire distributed system will not generate ID collisions (by the data center ID and machine ID to differentiate), and high efficiency, tested, snowflake per second can produce about 260,000 ID. */public class Snowflakeidworker {//==============================fields========================================= = =/** start time cut (2015-01-01) */private final long Twepoch = 1420041600000L; /** number of digits for machine ID */private final long workeridbits = 5L; /** the number of digits in the ID of the data ID */ Private final long datacenteridbits = 5L; The maximum machine ID supported by/**, the result is 31 (this shift algorithm can quickly calculate the maximum number of decimal digits that can be represented by several binary numbers) */private final long Maxworkerid = -1l ^ ( -1l << Workeridb its); The maximum data identification ID supported by/**, the result is */private final long Maxdatacenterid = -1l ^ ( -1l << datacenteridbits); /** the number of digits in the ID of the sequence */private final long sequencebits = 12L; /** Machine ID Move left 12 bits */private final long workeridshift = sequencebits; /** data ID ID 17 bits to the left (12+5) */private final long Datacenteridshift = sequencebits + workeridbits; /** time truncation to the left 22 bits (5+5+12) */private final long Timestampleftshift = sequencebits + workeridbits + datacenteridbits; /** generates a mask for the sequence, here is 4095 (0b111111111111=0xfff=4095) */private final long Sequencemask = -1l ^ ( -1l << sequencebits); /** Work Machine ID (0~31) */private long workerid; /** Data Center ID (0~31) */private long datacenterid; /** millisecond Sequence (0~4095) */private long sequence = 0L; /** time of last generation ID */private long lasttimestamp = -1l; //==============================constructors=====================================/** * constructor * @param workerid Work ID (0~31) * @param datacenterid data Center ID (0~31) */Public Snowflakeidworker (long Workerid, long Datacenterid) {if (Worke RId > Maxworkerid | | Workerid < 0) {throw new IllegalArgumentException (String.Format ("worker Id can ' t is greater than%d or less than 0 ", Maxworkerid)); } if (Datacenterid > Maxdatacenterid | | Datacenterid < 0) {throw new IllegalArgumentException (Str Ing.format ("Datacenter Id can ' t is greater than%d or less than 0", Maxdatacenterid)); } This.workerid = Workerid; This.datacenterid = Datacenterid; }//==============================methods==========================================/** * Get the next ID (the method is thread-safe) * @return Snowflakeid */public synchronized Long NextID () {Long timestamp = Timegen (); If the current time is less than the timestamp generated by the last ID, it should throw an exception 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 it is generated at the same time, the sequence in milliseconds if (Lasttimestamp = = timestamp) {sequence = (sequence + 1) & Sequenc Emask; Intra-millisecond sequence overflow if (sequence = = 0) {//block to next millisecond, get new timestamp timestamp = Tilnextmillis (lastt Imestamp); }}//timestamp changed, in milliseconds sequence reset else {sequence = 0L; }//Last generation ID time truncation Lasttimestamp = timestamp; Shift and pass or operate together 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 * @param lasttimestamp the last generation ID's time intercept * @return current timestamp */protected long TIlnextmillis (Long Lasttimestamp) {Long timestamp = Timegen (); while (timestamp <= lasttimestamp) {timestamp = Timegen (); } return timestamp; /** * Returns the current time in milliseconds * @return current time (msec) */protected long Timegen () {return system.currenttimem Illis (); }//==============================test=============================================/** Test */public static void Main (string[] args) {Snowflakeidworker idworker = new Snowflakeidworker (0, 0); for (int i = 0; i < i++) {Long id = idworker.nextid (); SYSTEM.OUT.PRINTLN (long.tobinarystring (id)); SYSTEM.OUT.PRINTLN (ID); } }}
Twitter's distributed self-increment ID algorithm snowflake (Java edition)