Testing & QA · 6 min read

UUID v4 vs v7 in 2026: Bit Layout, Sorting, Index Cost

UUID v4 vs v7 explained by their bits: 122 random bits against a 48-bit millisecond timestamp, sort behaviour, B-tree index cost, privacy, and encoding size.

By Charlie Simmon ·

On this page
  1. The 128-bit layout, side by side
  2. v4 in detail
  3. v7 in detail
  4. Sorting and monotonicity within a millisecond
  5. Index cost in a B-tree
  6. Privacy and the timestamp leak
  7. Storage and encoding size
  8. Choosing, migrating, and testing
  9. Questions

UUID v4 and UUID v7 are both 128-bit identifiers, and that is where the similarity ends. v4 spends 122 bits on randomness. v7 puts a 48-bit Unix millisecond timestamp in front, then 4 version bits, 74 random bits, and 2 variant bits. The version number is not a ranking. It is a statement about what the identifier is allowed to leak and how it sorts.

Both are defined in RFC 9562, which replaced RFC 4122 in 2024. The practical choice comes down to three questions: does the identifier need to sort by creation time, will it land in a B-tree primary key, and is it acceptable for the creation time to be readable by anyone holding the value. This guide answers those with the byte layout rather than a rule of thumb.

The 128-bit layout, side by side

Every UUID is 16 bytes, and the fields are defined by position, not by the textual dashes. Byte 6 contains the version in its high nibble. Byte 8 contains the variant in its top two bits. Everything else depends on the version. In the canonical string, the 13th hexadecimal character is the version and the 17th is the variant.

For v4, the 122 bits that are not version or variant are random. For v7, the first 48 bits are the Unix timestamp in milliseconds, big-endian, which is why v7 values sort in creation order as raw bytes. The remaining 74 bits are random by default, though RFC 9562 allows some of them to carry a counter for monotonicity within a millisecond.

FieldBitsv4v7
Bytes 0-548Randomunix_ts_ms, big-endian
Byte 6 high nibble40100 (version 4)0111 (version 7)
Byte 6 low nibble and byte 712Randomrand_a
Byte 8 top bits210x variant10x variant
Bytes 8-15 remaining62Randomrand_b
Total random bits12212274

v4 in detail

A v4 UUID is a random number with six fixed bits. The high nibble of byte 6 is 0100, so the 13th character of the canonical string is always 4. The top two bits of byte 8 are 10, so the 17th character is always 8, 9, a, or b. Those are the only predictable characters; the other 30 hexadecimal digits are random.

The randomness is the security property. A v4 value carries no timestamp, no sequence, and no machine identifier, so it reveals nothing about when or where it was created. That makes it a reasonable choice for a public identifier that must not be enumerable, provided it is generated by a cryptographically secure random source. A 122-bit space makes collisions negligible: a billion values give a collision probability of roughly 2^-63.

  • Version nibble: always 4 at position 13 of the canonical string
  • Variant: always 8, 9, a, or b at position 17
  • Random bits: 122, from a CSPRNG
  • Sorts by nothing meaningful; order is random
  • Reveals no creation time and no sequence

v7 in detail

A v7 UUID is time-ordered. Bytes 0 through 5 hold the number of milliseconds since the Unix epoch, big-endian. The high nibble of byte 6 is 0111. The low nibble of byte 6 and all of byte 7 form rand_a, the top two bits of byte 8 are the variant, and the remaining 62 bits are rand_b. That is 74 random bits plus a timestamp that is readable by inspection.

The timestamp is 48 bits, which is enough for milliseconds until the year 10889, so overflow is not a practical concern. Two v7 values created in the same millisecond differ only in their random or counter bits, and a value created later always compares greater as a byte string than one created earlier, as long as the clock does not go backwards. That ordering is the whole point.

ByteContentExample
0-5Unix milliseconds, big-endian0192f3c4a1b2
6 high nibbleVersion 77
6 low nibble + 7rand_a, 12 bitsa1b
8 top 2 bitsVariant 108, 9, a, or b
8 (low 6) to 15rand_b, 62 bitsRandom or counter

Sorting and monotonicity within a millisecond

Lexicographic sorting of the canonical text matches byte order, because the hyphens sit at fixed positions and hexadecimal digits sort in the same order as their values when lowercase. Sort by the text form or by the 16 bytes and you get creation order. That holds across machines without coordination, which is why v7 is attractive for event logs and for identifiers that become part of a sort key.

The gap is within a single millisecond. By default, two values created in the same millisecond are ordered by their random bits, which is not creation order. RFC 9562 describes using the rand_a field, or the leftmost random bits, as a counter seeded randomly at each timestamp tick. With that method, values are monotonic for practical purposes within a millisecond, at the cost of a little less randomness and a shared counter that must be handled carefully by concurrent producers.

  • Across milliseconds: ordering is guaranteed while the clock is monotonic
  • Within a millisecond: random order unless a counter is used
  • Counter method: seed randomly per tick, increment per value
  • Clock rollback: can break ordering, so use a monotonic clock source
  • Concurrency: increment the counter atomically or lose the property

Index cost in a B-tree

A random v4 primary key inserts at a random position in the index. On a B-tree that means touching a different leaf page on almost every insert, evicting pages from the buffer pool, and splitting pages in the middle, which leaves them half full. The index grows larger, the write amplification rises, and cache hit rates fall as the table grows.

A v7 key inserts near the right edge, so the working set stays small and page splits happen at the end where they are cheap. The trade-off is contention: every writer wants the same rightmost page. On a single database that is usually fine with a few hundred writes per second; at very high concurrency it becomes a hotspot, and the usual mitigations are batching, connection-level interleaving, or a small amount of jitter in the high bits.

PropertyRandom v4 keyTime-ordered v7 key
Insert locationRandom leaf pageRightmost leaf page
Page splitsFrequent, mid-pageRare, at the end
Buffer pool localityPoor on large indexesGood
Index sizeLarger, lower fill factorCompact
Write hotspotSpread across the treeConcentrated on the right edge
Range scan by timeRequires a separate columnNative

Privacy and the timestamp leak

A v7 value tells anyone who holds it roughly when it was created, to the millisecond, and therefore how many other records were created around that time. That is fine for internal keys and event ids. It is not fine for a public identifier that should not reveal signup volume, order rate, or the age of an account.

If you need both ordering and opacity, the options are to keep v7 internal and expose a separate random public id, or to use v4 for anything that leaves your system. Do not treat a UUID as a capability token in either version; both are identifiers, not authorization. A v4 id is unguessable in practice, but it still must be checked against access control on every request.

  • v7 reveals creation time to the millisecond
  • Enumerating nearby v7 values reveals record volume
  • Keep v7 internal, expose v4 or a separate public id
  • Neither version is an access token or a secret
  • Randomized v7 is not a thing; the timestamp is mandatory

Storage and encoding size

Sixteen bytes is the storage answer for both versions. The canonical text form is 36 characters, of which 32 are hexadecimal digits and 4 are hyphens. Storing the text form doubles the size and slows comparisons, so use a native UUID type where one exists, BINARY(16) where one does not, and format only at the boundary.

SQL Server has a wrinkle worth testing: its uniqueidentifier type sorts in a byte order that is not the same as the canonical string, and the internal byte order differs from RFC 4122. Code that relies on v7 ordering must verify the sort behaviour on that platform rather than assuming it. Where you need a compact text form, base64url without padding gives 22 characters, but it is not a standard UUID representation and will confuse tooling that expects the canonical form.

RepresentationSizeNotes
Binary16 bytesNative storage, fastest comparison
Canonical text36 characters8-4-4-4-12 with hyphens
Hex without hyphens32 charactersUsed in some APIs and logs
Base64url, no padding22 charactersNot a standard UUID form
Base3226 charactersRare, used by some ticketing systems

Choosing, migrating, and testing

Pick v7 when the identifier is a database key, when you want to sort by creation time without a second column, or when you are building an append-heavy table. Pick v4 when the value is exposed publicly, when creation time is sensitive, or when the system already stores a timestamp column and gains nothing from ordering. Both are correct choices for different constraints, and mixing them in one table is usually a mistake.

Testing needs to assert structure rather than values. Check the version nibble, the variant nibble, and the byte length after parsing. For v7, assert that the embedded timestamp falls within the test window and that two values created in order compare correctly. Never snapshot a literal UUID in a test, and never assert that a v7 is strictly greater than the previous one unless the generator uses a counter; without one, same-millisecond order is random.

SituationPreferReason
Primary key on a large tablev7Locality and smaller index
Public-facing identifierv4No timestamp disclosure
Event log sorted by timev7Native ordering without a column
Session or reset tokenNeitherUse a purpose-built random token
Existing table with a time columnv4Ordering already available
Cross-system interchangeEitherBoth are standard 128-bit UUIDs

Questions

Are UUID v4 and v7 the same size?+
Yes. Both are 128 bits, 16 bytes, and 36 characters in canonical text. Only the internal layout differs.
Does UUID v7 sort correctly?+
Across milliseconds, yes, as raw bytes and as canonical text. Within a single millisecond the default order is random unless the generator uses a counter.
Which is better for a database primary key?+
v7, because inserts stay near the right edge of the B-tree instead of scattering across it. The trade-off is a write hotspot on the last page at very high concurrency.
Does UUID v7 leak information?+
It reveals the creation time to the millisecond, and nearby values reveal record volume. Keep it internal or use v4 for identifiers that leave your system.
Can I use a UUID as an access token?+
No. A UUID is an identifier. Even a v4 value must be checked against access control on every request, and a token should come from a purpose-built generator with its own lifetime rules.

Examples are synthetic. Do not use a generated address for identity checks, account registration, or delivery.

Reference standards

Read next

All guides