v4 and v7 are both 128-bit values, both are very unlikely to collide, and code that treats a UUID as an opaque string can't tell them apart. The difference is in what the bits mean, and so in how the values sort.
v4: random, no order
A version 4 UUID is 122 random bits from a cryptographically secure generator. The other 6 bits are fixed to mark the version and variant, per RFC 4122. There's no timestamp, no counter, nothing about when or where it was made. Two v4s generated a millisecond apart look as unrelated as two generated a year apart. That's what you want if IDs shouldn't be linkable. It's a problem if you use v4 as a database primary key.
The problem comes from how B-tree indexes handle inserts. B-trees are the default index in Postgres, MySQL/InnoDB, and most relational databases, and they do best when new keys arrive roughly in order, so each insert lands at or near the right-hand edge of the tree. Random v4 values land all over the key range instead. Inserts can split pages anywhere in the tree, and the set of pages the database has to keep in memory grows to cover the whole index rather than just its tail.
On a small table you won't notice. Once insert throughput matters, it's a measurable and well-documented cost, and it comes from the values being unsorted, not from UUIDs being 128 bits wide. A 128-bit key that arrived in order wouldn't have this problem, which is what v7 provides.
Why random keys make inserts expensive
A B-tree keeps its leaf pages in key order, and each page holds a fixed number of bytes. With keys that only ever increase, as with a sequential integer or a v7 UUID, every insert goes to the rightmost leaf. It fills up, splits, and the new rightmost page takes the next inserts. The rest of the tree isn't touched, and the database only needs the tail of the index in its buffer cache to keep up.
A v4 key has nothing to do with insertion order, so each one lands somewhere arbitrary in the existing key range. Two things follow. First, the page it belongs on could be any page in the index, so avoiding a disk read per insert means keeping much more of the index in memory. Second, page splits happen all over the tree instead of at one edge. Each split is a write of its own (rewrite the page, allocate a new one, update the parent), and spreading them around defeats caching that assumes recently used pages stay hot.
This comes from how B-trees keep keys sorted, not from any one engine's implementation. It shows up the same way in Postgres, MySQL/InnoDB, and SQL Server.
v7: timestamp first, still a valid UUID
Version 7, finalized in RFC 9562 in 2024, rearranges the bits. The first 48 are a Unix timestamp in milliseconds. The rest, apart from the fixed version and variant bits, are random, as in v4. The values sort by creation time (ties within one millisecond fall back to random order), and they're still spec-compliant, globally unique 128-bit UUIDs. Any UUID-typed column or UUID parser accepts them as they are.
018f4d2a-7b3e-7c21-9f4a-3e2b1c8d9a0f 018f4d2a-7b3e-7c22-8a1b-0f9e8d7c6b5a 018f4d2a-8c1f-7d33-b2a4-1a2b3c4d5e6f // first 48 bits (before the version nibble) increase with time — // these three sort in creation order even though the rest is random
You can read the timestamp off the examples. It's the first 12 hex digits, ignoring the hyphen: 018f4d2a7b3e for the first two IDs and 018f4d2a8c1f for the third. The first two share a millisecond, so their relative order comes down to random bits (more on that below). The third was generated later, so it sorts after both.
For a primary key or any time-ordered index, this fixes the insert problem. New rows land at the end of the index, the same pattern you'd get from an auto-incrementing integer. And you keep global uniqueness without a central sequence, which is why people wanted UUID primary keys to begin with.
v1 has a timestamp too
Version 1 is decades older than v7 and also encodes time, but in a way v7 was designed to replace. v1's 60-bit timestamp (100-nanosecond intervals since 1582-10-15, per RFC 4122) is split across three fields: time_low, time_mid, and time_hi_and_version. RFC 4122 puts time_low, the least-significant part of the timestamp, first in the string form. So comparing two v1 UUIDs as strings or bytes does not sort them by time, even though the full timestamp is in there. You'd have to pull out the three fields and reassemble them to get chronological order.
v1 also has a 48-bit "node" field, traditionally the machine's MAC address. That leaks which physical machine generated the ID. Browsers can't read a MAC address, so this site's v1 generator, like every browser-side v1 implementation, fills the node field with random bits. RFC 4122 allows that when no hardware address is available, with the multicast bit set to show it isn't a real MAC.
RFC 9562 was written partly to fix this. v7 puts the whole timestamp in one contiguous, correctly ordered field at the front, so plain string or byte comparison sorts correctly, and there's no MAC-shaped field to leak or fake. If you chose v1 for rough time ordering, v7 is better in every respect. It sorts, and v1 never did.
One limit on v7: the timestamp only goes down to milliseconds, and everything below that is random. RFC 9562 describes optional ways to keep IDs strictly ordered within a millisecond (a counter, or constrained random bits), and an implementation may use them. A basic generator that fills the remaining bits with fresh random data on each call, which is what this site's UUID Generator does, doesn't. Two v7s generated within the same millisecond can come out in either order. You get correct ordering at millisecond granularity, not a strict total order for IDs generated faster than that.
When to stick with v4
Sorting by creation time is useful until it leaks information. Anyone who can compare two v7 UUIDs from a public URL or API response can tell which resource came first, and roughly when, to the millisecond. For an internal primary key that rarely matters. For an ID users see (an invite code, a share link, an account ID) it can. A competitor can estimate your signup rate from consecutive account IDs. A user can guess that an ID created moments before theirs belongs to someone in particular. v4 gives none of that away, because nothing in the ID says when it was created.
So: use v7 for internal database keys, where insert performance and index locality matter. Use v4 for IDs exposed outside the system, where creation order shouldn't be guessable. A raw timestamp or an auto-incrementing integer doesn't replace v7 in the first case. Neither is globally unique across systems without central coordination, and an auto-increment integer tells anyone who sees one value roughly how many rows you have. v7's random low bits don't.