retrystorm · Learn · Interview practice · Glossary · Components

Home › Learn › Unique IDs

Chapter 26 · Data at scale · 25 min

Unique IDs

Counters, ranges, UUIDs, Snowflake IDs and hashes: minting unique codes on many servers at once, and how long each one is.

ink.ly, the link shortener you sketched on the whiteboard, is finally being built. Before anything else, every link needs a code no other link has, and every app server will be minting them at once. A partner is already lined up to import its links on day one.

Every new link, post or order needs a name nothing else has. On one server that's a counter. On many servers at once, handing out names without two of them ever picking the same one is a design decision of its own.

Why it's hard

One app server can count: 1, 2, 3. A dozen app servers can't each count on their own, or two of them will both hand out 1. They need either one shared source of numbers, or a way to pick names that can't collide without asking anyone.

Every replica mints codes at once. None may ever repeat.

A database's auto-increment column is the shared-source answer: it works, but every new row then waits on the one machine that holds the counter, and a database spread over many machines has no single counter at all.

Four strategies

StrategyHow it worksLength in base62The catch
Central counterone service hands out 1, 2, 3...7 characters for 3.5 trillionevery write waits on it, and fails with it
UUID128 bits, 122 of them random (v4)22 characters in base62, 36 in the usual formlong, and random order
Snowflaketime, machine and sequence in 64 bits11 characterseach machine needs its own number
Hash-baseda hash of the URL, cut shortas short as you likecollisions mean another attempt

Base62 writes a number with the digits, lowercase and uppercase letters, 62 symbols in all. Each character multiplies the space by 62: 6 characters give 57 billion codes, 7 give 3.5 trillion. For a link shortener, short codes are the product, so the strategy's length matters as much as its speed.

A central counter

A counter service gives the shortest codes and never collides. The price is that every write calls it. As writes approach what it can hand out each second, calls wait longer, and if it goes down, nothing new can be created at all.

The usual fix keeps the counter but calls it less: each app server takes a range of numbers at a time (say 1 000), and hands them out locally. The counter sees one call per thousand writes. The cost is gaps: a server that crashes with half a range unused leaves those numbers behind forever, which is harmless for links. How big should a range be?

Snowflake IDs

A Snowflake ID (named after Twitter's generator) packs three fields into 64 bits (63 of them are used, plus an unused sign bit):

BitsFieldGives
41milliseconds since a start dateabout 69 years of time
10machine numberup to 1 024 generators
12sequence within the millisecond4 096 IDs per millisecond per machine

Two machines can't collide because their machine numbers differ, and one machine can't collide with itself because time and the sequence only move forward. A machine that uses up all 4 096 sequence numbers within one millisecond waits for the next millisecond. IDs come out roughly in time order, so new rows land at the end of a table sorted by ID, and you can sort by ID instead of by date. No call to a shared service is needed per ID. The catches: machine numbers have to be assigned once, usually from a config file or a small shared service at startup, and a clock that jumps backwards must make the generator wait.

Try the clock yourself: one machine's clock steps back, and the generator either trusts it or waits.

Hashing the URL

Hash the long URL and keep the first 7 characters. The same URL always gets the same code, which deduplicates for free, and no service has to be called. But cut-short hashes collide: two different URLs can share a prefix. So you insert each new code into a column the database keeps unique: it rejects a code that is already taken. On a rejection, first read the row that holds the code: if it's the same long URL, someone already shortened it, and you reuse its code. Only a different URL means a real clash, and then you try again with a salt added. Checking first ("is this code taken?") and then inserting isn't enough, because two servers can both check, both see the code free, and both insert it. As the table fills, clashes get more common.

Codes in use, out of 3.5 trillionChance a new 7-character hash clashes
1 millionabout 1 in 3.5 million
1 billionabout 1 in 3 500

Choosing

You needChoose
The shortest codes, and writes you can route through one servicea counter, handing out ranges
No coordination at all, length doesn't matterUUID
Time-ordered 64-bit IDs from many machinesSnowflake
The same code for the same inputa hash, with collision checks

In the wild

The ideas in this chapter, and where you'll meet them in real systems and interviews:

IdeaWhere you'll meet it
Snowflake IDsTwitter's Snowflake, and Discord and Instagram's variants
UUIDsUUID v4 (random) and v7 (time-ordered), ULIDs
Countersdatabase sequences; Flickr's "ticket servers", a central counter split over two machines

Your practice

ink.ly launches next week, and a partner wants to import its links on day one: thousands of new short codes a second, from every app server at once. Codes have to stay short, so UUIDs are out. Pick how codes are minted so creating a link stays fast during the import.

Key takeaways

  • Four ways to mint IDs: counter, UUID, Snowflake, hash. Each trades length, coordination or collisions.
  • A central counter is short and never collides, but every write waits on it; ranges cut the calls.
  • Snowflake IDs pack time, machine and sequence into 64 bits: no coordination, roughly time-ordered.
  • Hashed codes dedupe for free, but cut-short hashes collide: insert into a unique column and retry on a clash.

Read first

  • Scaling Out

Practise it

  • Mint the Codes: Hand out short, unique codes from many app servers at once, fast enough for a bulk import.

Terms

  • Base62: Writing numbers with digits and both cases of letters, 62 symbols, so codes stay short.
  • Snowflake ID: A 64-bit id made of time, machine number and sequence, roughly in time order.
  • UUID: A 128-bit id that needs no coordination; the common version 4 is random. 36 characters in its usual form, 22 in base62.
About · Contact · Privacy · Terms of use