The appeal of a UUID is simple: two machines that have never spoken can each mint one and be confident they won't collide, so you don't need a central counter handing out IDs. That's worth a lot in distributed systems and offline-capable apps. The versions are really a short history of fixing each other's problems, and knowing that history is enough to choose well. You can generate any of them on the UUID generator; this guide is about which to reach for.
From v1's privacy leak to v7
v1 came first and builds the ID from a timestamp plus the machine's MAC address. That makes it roughly time-ordered, but it also bakes in which computer generated it and when — information you rarely meant to publish. This isn't a hypothetical: the author of the Melissa virus was famously traced partly through a v1 UUID embedded in a document. v4 answered by throwing determinism out entirely and filling the ID with random bits. That killed the leak and made v4 the default everyone reaches for, at the cost of being completely unordered. v7, standardized in 2024's RFC 9562 (which also tidied up v6 and v8), is the modern compromise: a millisecond timestamp at the front for ordering, random bits filling the rest for uniqueness. For ordinary application code, v4 remains the right, boring answer. v7 is the one to remember the moment a UUID is going to be a primary key.
You can read a UUID's version straight off the string. In 3f2504e0-4f89-41d3-9a0c-0305e82c3301, the first digit of the third group (4) is the version, and the first digit of the fourth group (9, normally one of 8/9/a/b) is the variant. That one version digit is all you need to tell a random v4 from a time-ordered v7 at a glance.
Why random keys make inserts slow
This is the practical reason v7 exists, and it surprises people who assume "an ID is an ID". Most databases store rows in a B-tree keyed by the primary key, and many — InnoDB in MySQL is the textbook case — physically cluster the table data in primary-key order. With a sequential integer or a v7 UUID, every new row lands at the end of that order, appending cleanly to the last page. With a random v4 key, each insert lands at a random spot, forcing the engine to split pages and shuffle data, while the cache thrashes because you're touching pages all over the index instead of a hot tail. On a small table you'll never notice. On a large, write-heavy one, random keys can cut insert throughput dramatically and bloat the index.
v7 sidesteps all of it by being monotonic-ish: because the timestamp leads, values mostly increase over time, so new rows append the way an auto-increment would — while keeping the "generate anywhere, never collide" property that made you want a UUID in the first place. If you're choosing identifiers for a high-insert table, this is the single best reason to prefer v7 over v4.
Don't store a UUID as a 36-character string if volume matters. A UUID is 16 bytes; written out as text with its dashes it's 36 characters. On a big table that waste shows up twice — in row size and in every index that references the key. Store it as BINARY(16) in MySQL or use Postgres's native uuid type, and your indexes get meaningfully smaller and faster.
ULID, KSUID and Snowflake
UUIDs aren't the only game in town, and a few alternatives solve the same "sortable, unique, generated anywhere" problem with different trade-offs. ULID is a popular one: like v7 it's time-ordered, but it encodes to a 26-character Crockford base32 string that's more compact and URL-friendly than a UUID's hex. KSUID is similar with a different layout and a longer timestamp range. Snowflake IDs (originally Twitter's) pack a timestamp, a machine ID and a sequence counter into a 64-bit integer, which is half the size of a UUID and fits in a BIGINT column, at the cost of needing coordinated machine IDs. If v7 had existed when these were designed, fewer of them would have been built — but ULID in particular is still worth knowing when you want time-ordering and a shorter string.
Are they really unique, and should users see them?
The collision worry is almost always misplaced. A v4 UUID has 122 random bits, which is so much space that you would need to generate billions per second for many years before a collision became remotely likely. For any normal application you can treat them as unique without a second thought. What's more worth thinking about is whether humans should ever see them. UUIDs are long, ugly in a URL, and impossible to read over the phone, so they make poor user-facing identifiers — an order number a customer reads aloud should not be a 36-character hex string. Keep UUIDs for internal references and system-to-system communication, where their generate-anywhere property is exactly what you want, and use something short and friendly for anything a person has to type or quote.
One more note on v4 specifically: unguessable is not the same as secret. A random UUID is fine as an identifier an attacker can't easily enumerate, but it isn't a credential. For a password-reset link or a session token, generate a purpose-built cryptographic random value rather than leaning on a UUID to do a security job it wasn't designed for.
The short version: reach for v4 by default, switch to v7 (or ULID) the moment the ID becomes a database key, store it as bytes when volume is real, and keep something human-friendly for the IDs your users actually see.