Shard scene including a plurality of distributed system, while at the respective Shard insert data, how to generate the global data is unique ID? In a standalone system (e.g. a MySQL instance), generates a unique ID is very simple, MySQL comes with the direct use of the self-energizing ID function can be achieved.

However, a plurality of Shards distributed system (e.g., multiple instances a MySQL cluster, this cluster is inserted in the data) is present, the problem becomes complicated, the generated global unique ID to meet the following requirements:

  • Unique, globally generated unique ID to ensure
  • The next data migration is not limited by the ID generating mode among the plurality of Shards
  • Ordering, resulting in the best energy bands time ID information, for example the k-bit ID is Timestamp, so by directly ordering the k-bit ID data to the chronological
  • Generated ID preferably not more than 64 bits
  • Availability, speed of the ID generating requirements. For example, in a high throughput scenario, it is necessary to generate tens of thousands per ID (latest peak on Twitter reached 143,199 Tweets / s, i.e. 100,000 + / sec)
  • The best overall service with no single point

In front of 6:00 to meet the requirements of the scene, how to generate a global unique ID it?

Database increment ID

A single database table, used auto incrementto generate a unique global incremented ID.

Additional advantage is that no additional operations, growth in fixed-length, single-table structure is unique, is the disadvantage of poor performance under high concurrency, the upper limit is the upper limit of the production database server single, horizontal expansion difficult, distributed database, can not be guaranteed to be unique .

UUID

Without these restrictions the above, the problem is relatively easy, for example: direct use UUID.randomUUID () interface to generate unique ID (http://www.ietf.org/rfc/rfc4122.txt), but the program has generated ID. 128 bits, addition, no ID generated with Timestamp own UUID general programming language implemented, Java in the UUID.randomUUID().toString()ID generation is not dependent database implementation.

Advantage is locally generated ID, no need to invoke a remote, globally unique, a good level of scalability. Disadvantage is that, ID There are 128 bits long, large space, generate the string type, low efficiency index, ID generated with Timestamp can not guarantee no time increments.

Flickr global primary key

Flickr approach 1 is using MySQL increment ID, and replace into syntax. ID But he did not bring the program ID, Timestamp, resulting not chronological

Create a 64-bit incrementing ID, first create the table

CREATE TABLE `Tickets64` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `stub` char(1) NOT NULL default '',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `stub` (`stub`)
) ENGINE=MyISAM

SELECT * from Tickets64 Suppose there is a row in table

+-------------------+------+
| id                | stub |
+-------------------+------+
| 72157623227190423 |    a |
+-------------------+------+

If you need to generate a new global 64 bits of ID, as long as the implementation of SQL:

REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();

Globally Unique ID SQL returned ID is to be produced. Use REPLACE INTOinstead of the INSERT INTObenefit is to avoid too many table rows. stub To set a unique index.

Flickr run inside two ticket servers, backup and load balancing call the shots through two machines.

TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1

TicketServer2:
auto-increment-increment = 2
auto-increment-offset = 2

Twitter Snowflake

Twitter use Zookeeper achieve a global ID generation service Snowflake: https://github.com/twitter/snowflake

Snowflake composition of the generated unique ID (from the higher to lower):

  • 41 bits: Timestamp millisecond
  • 10 bits: 节点 ID datacenter ID 5 bits + worker ID 5 bits
  • 12 bits: sequence number

A total of 63 bits, the highest bit is 0

unique ID generation process:

  • 41 bits of Timestamp: every time you want to generate a new ID, that they will get what the current Timestamp, then two cases generated sequence number:

      - 如果当前的 Timestamp 和前一个已生成 ID  Timestamp 相同 (在同一毫秒中), 就用前一个 ID  sequence number + 1 作为新的 sequence number (12 bits); 如果本毫秒内的所有 ID 用完, 等到下一毫秒继续 (**这个等待过程中, 不能分配出新的 ID**)
      - 如果当前的 Timestamp 比前一个 ID  Timestamp 大, 随机生成一个初始 sequence number (12 bits) 作为本毫秒内的第一个 sequence number
  • 10 bits of the machine number, assigned at the time of start Worker ID, get (to ensure that all of the Worker would not duplicate machine number) from a cluster Zookeeper

Throughout the process, only time will start Worker externally dependent (need to get from Zookeeper Worker number), then you can work on your own, did to the center.

Exceptions discussed:

When obtaining the current Timestamp, if before getting to the time stamp than a generated ID of Timestamp smaller how to do? Snowflake approach is to continue to get the current time of the machine until obtaining greater Timestamp to continue to work (in this during the waiting period, it can not be assigned a new ID)

As it can be seen from this abnormal situation, if the Snowflake those machines running clock has a large deviation, the entire system does not work Snowflake (waiting time deviation, the more, the longer assigned a new ID)

Snowflake can also be seen from the official documents (https://github.com/twitter/snowflake/#system-clock-dependency), which explicitly requires "You should use NTP to keep your system clock accurate". And the best the NTP is not configured to adjust the backward mode. in other words, when NTP correct time, the machine will not call back the clock backwards.

The following are other variants of Snowflake, the method also learn from Instagram produce ID Snowflake

Boundary flake

Code Address: https://github.com/boundary/flake

Variety:

ID expanded to a length of 128 bits:

  • Up to 64 bits timestamp;
  • Worker then 48 bits of resolution (and as long as the Mac address);
  • Finally, 16 bits of Seq Number

Because it uses 48 bits as the Worker ID, Mac address and length of the same, and do not need to get Zookeeper communications Worker ID when started. Done completely decentralized

Based on Erlang, aim is to achieve a smaller probability of collision with more bits, thus supporting more Worker simultaneously. At the same time, it can be allocated more every millisecond's ID.

Simpleflake

Source: https://github.com/SawdustSoftware/simpleflake

The idea is to remove Simpleflake Worker number, the reserved 41 bits Timestamp, while the extended sequence number to 22 bits;

Simpleflake features:

  • It relies entirely randomly generated sequence number (this also leads to possible duplication generation ID)
  • Worker No. no, there is no need Zookeeper and communication, to achieve a fully decentralized
  • Timestamp remain consistent and Snowflake, the future can be seamlessly upgraded to Snowflake

Simpleflake problem is completely random sequence number generation, results in the formation of the ID may be repeated. This generated ID number with increasing probability of repetition of the second generation ID and growth.

Therefore, Simpleflake limitation is not much generated per ID (preferably less than 100 times / sec, if more than 100 times / second scenario, Simpleflake not apply, it is recommended to switch back to Snowflake).

Instagram practice

Instagram Flickr reference program, combined with the Twitter experience, take advantage of the characteristics PostgreSQL database, to achieve a more simple and reliable ID generation services. Instagram distributed storage solution: Table to each slice into a plurality of logic (logic Shard), the number of logical fragmentation can be large, e.g. fragments logic 2000. Then develop a rule that each logical patch which are stored in the database instance thereon; database instance does not require much For example, there are two instances of the PostgreSQL system (using Instagram PostgreSQL);. May be used to store odd logic slice of a database instance, the even slice stored logical database instance to the second rule

Table Each field specifies a field as a fragment (e.g., the user table can be specified as uid fragment field)

When inserting a new data, according to the value of the first fragment field, which is assigned to determine the data slice logic (logic Shard) then according to the correspondence logic Shard and PostgreSQL instances, this determination on to which data should be stored PostgreSQL instance

Instagram ID consideration in the design of the following factors:

  • Generated IDs according to the time required to sort, query such as when a group of photos you do not need to obtain additional photos more information to be sorted
  • IDs 64bits index, or stored in the Redis
  • The system should introduce as few new ‘moving parts’ as possible — a large part of how we’ve been able to scale Instagram with very few engineers is by choosing simple, easy-to-understand solutions that we trust.

Instagram unique ID consists of:

  • It represents 41 bits Timestamp (milliseconds), a start time epoch can customize
  • 13 bits representing each of the logic Shard code (up to 8 x 1024 th logic Shards)
  • Represents 10 bits sequence number; each Shard ID 1024 may be generated every millisecond

Suppose September 2011 No. 9 5:00 o'clock, epoch began on January 1, 2011, it has been 1387263000 milliseconds elapsed, then the first 41 bits is

id = 1387263000 <<(64-41)

Next, 13 is determined by the fragment ID, user ID is assumed to follow fragmentation, fragmentation 2,000 logic, if the user ID is 31341, then the fragment ID is 31341%2000 -> 1341, the next 13 bits are:

id |= 1341 <<(63-41-13)

Finally, the self-energizing each table to fill the remaining bits, assumed to have been generated IDs table 5000, then the next value is 5001, then modulo 1024

id |= (5001 % 1024)

sequence number using auto-increment sequence on each PostgreSQL Table is generated. If you already have 5000 records on a current table, then the next auto-increment sequence table is 5001 (direct call PL method PGSQL provided / can be acquired) and then this 5001 1024 modulo get the 10 bits of the sequence number.

Instagram advantage of this solution is that:

Worker numbers used to replace the use of logic Shard Snowflake number, the central node does not need to obtain a number Worker. Done completely decentralized

Another benefit that comes with that, ID can directly know this record is stored on which logic Shard. Meanwhile, in the future to do data migration time, but also by logic Shard do data migration as a unit, so this practice will not affect future data migration

MongoDB ObjectID

The ObjectID MongoDB 2 uses 12 bytes in length, including a time stamp coding.

  • Wherein the first four bytes of the time stamp, starting from the standard epoch, in seconds, the time stamp and the 5 bytes to ensure the uniqueness of the second level, in order to ensure the insertion time sequence.
  • Then behind the first four bytes of the three byte time stamp machine number, the three bytes of the same host as a unique identification, typically machine name hash value.
  • Next two bytes are identified by a PID, the same machine may run multiple instances Mongo, to ensure that no conflict with PID
  • After three bytes incremented sequence number, incrementing counter, to ensure that the same second ObjectID generated conflict does not appear, allowing cubic 16777216 256 records.

reference

  1. http://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/ 

  2. https://docs.mongodb.com/manual/reference/method/ObjectId/#objectid 

Original Address: http: //einverne.github.io/post/2017/11/distributed-system-generate-unique-id.html