Concept · Consistency & coordination

Unique ID generation

UUIDv4, UUIDv7, Snowflake IDs and ticket servers: uniqueness, ordering and index locality.

3 minSnowflakeUUIDULIDticket server

Every record needs an ID. A single database sequence is the simplest answer until IDs must be generated on many machines, by clients, or before a row reaches the database.

SchemeSizeTime-orderedNeeds coordinationNotes
Database sequence (bigserial)64 bitsYesThe databaseSimple; reveals volume ("order #1043"); one writer
UUIDv4128 bitsNoNoRandom; inserts scatter across B-tree indexes
UUIDv7128 bitsYes (ms prefix)No48-bit timestamp + random bits; RFC 9562
ULID128 bitsYesNoSimilar to UUIDv7, base32 text form
Snowflake64 bitsYes (ms)A unique worker ID per process41 bits time, 10 bits worker, 12 bits sequence
Ticket server / ranges64 bitsRoughlyOccasionallyEach node reserves a block (say 1,000) of a central counter

Why ordering matters for performance

B-tree indexes keep keys sorted. Random keys (UUIDv4) insert on random pages, so the whole index must stay in memory and pages split constantly. Time-ordered keys append near the right edge of the index, like a sequence, which keeps inserts cheap. For new systems, UUIDv7 is a good default: no coordination, sortable, widely supported.

Snowflake

text
| 1 bit 0 | 41 bits: milliseconds since a custom epoch | 10 bits: worker | 12 bits: sequence |
→ ~69 years, 1,024 workers, 4,096 IDs per millisecond per worker

Compact and fast, but every running process needs a unique worker ID (leased from a coordination service, or taken from a stable ordinal), and the generator must refuse to issue IDs if the clock moves backwards.

What IDs don't promise

  • Time-ordered IDs from different machines are only roughly ordered: clocks differ by milliseconds (clocks and ordering).
  • Sequential IDs are guessable. For public identifiers (short links, invoice URLs), use random or scrambled IDs and still check authorisation.

See keyset pagination for using ordered IDs as cursors.

Learn it in depth