Definition
- Approximate ordering – the time prefix comes from each node's wall clock; clock skew across nodes means UUIDv7 is not a reliable distributed sequence or global causal order.
- Compact storage – store as
uuid(PostgreSQL) orBINARY(16)(MySQL), not asCHAR(36), which roughly triples storage and bloats every index. - Time leakage – the leading 48 bits are a readable creation timestamp, so a UUIDv7 reveals when the record was created and must never be used as a secret, session token, or authorization credential.
- Within one node – ids are reliably increasing; the ordering concern is only across multiple generators.
- Separate token – if you need both an ordered key and an opaque public token, use UUIDv7 internally and issue a cryptographically random token for security.
A UUIDv7 reference number at National Bank of India has three warning labels. First, each branch stamps the number using its own wall clock, so if two branches' clocks disagree, you cannot trust the numbers to tell you which transaction truly happened first. Second, write the number in its compact binary form, not as a long 36-character string, or you waste filing space. Third, the number openly contains the timestamp, so anyone reading it learns when the record was created: it can identify a record but must never act as a password.
Understanding the Concepts
What is UUIDv7 and why is it popular?
UUIDv7 embeds a Unix timestamp in the most significant 48 bits, followed by random bits. This makes the ids roughly time-ordered, which gives B-tree index locality and avoids the random-insert fragmentation of UUIDv4.
package com.bank.model;
import java.util.UUID;
public class UUIDv7Demo {
public static void main(String[] args) {
// Java 17+ does not have native UUIDv7; use a library
// Example using com.fasterxml.uuid:java-uuid-generator
// UUID id = Generators.timeBasedEpochGenerator().generate();
// The first 48 bits are the Unix timestamp in milliseconds
// Remaining bits are random
// Result: 019234ab-cdef-7xxx-xxxx-xxxxxxxxxxxx
}
}
Why is UUIDv7 not a distributed sequence?
The time prefix comes from the generating node's wall clock. Within one node, ids are reliably increasing. But across nodes, clocks drift.
Node A clock: 10:00:00.000 ──▶ generates UUID_A Node B clock: 10:00:00.003 ──▶ generates UUID_B (3ms later by B's clock) But what if Node A's clock is 5ms ahead of true time? Node A actual: 09:59:59.995 ──▶ UUID_A timestamp says 10:00:00.000 Node B actual: 10:00:00.003 ──▶ UUID_B timestamp says 10:00:00.003 UUID_A < UUID_B by timestamp, but UUID_B was actually created AFTER UUID_A Clock skew: 5ms difference makes the ordering unreliable across nodes
For true ordering, use an explicit authoritative timestamp column, a real sequence, or logical clocks.
Why must UUIDv7 be stored compactly?
A UUID is 128 bits (16 bytes). Stored as CHAR(36), it becomes 36 bytes plus overhead: more than double the size.
-- Wrong: CHAR(36) wastes space and bloats indexes
CREATE TABLE transactions (
id CHAR(36) PRIMARY KEY, -- 36+ bytes per row and per index entry
amount NUMERIC(18,2)
);
-- Correct in PostgreSQL: native uuid type (16 bytes)
CREATE TABLE transactions (
id UUID PRIMARY KEY, -- 16 bytes
amount NUMERIC(18,2)
);
-- Correct in MySQL: BINARY(16)
CREATE TABLE transactions (
id BINARY(16) PRIMARY KEY, -- 16 bytes
amount DECIMAL(18,2)
);
Since the primary key is embedded in every secondary index in InnoDB, a CHAR(36) key bloats the entire table's index footprint and wastes buffer-pool memory.
How much storage difference does it make?
Rajesh measured at National Bank of India with 10 million transactions.
┌──────────────┬──────────────┬───────────────┐ │ Storage Type │ PK Size │ Index Overhead│ ├──────────────┼──────────────┼───────────────┤ │ BINARY(16) │ 160 MB │ +16 bytes/row │ │ CHAR(36) │ 360 MB │ +36 bytes/row │ │ Difference │ +125% │ +125% per idx │ └──────────────┴──────────────┴───────────────┘
Convert to text only at the application edge for display.
Why does UUIDv7 leak creation time?
The first 48 bits are a plainly decodable Unix timestamp in milliseconds.
package com.bank.util;
import java.time.Instant;
import java.util.UUID;
public class UUIDv7TimeExtractor {
public static Instant extractTimestamp(UUID uuid) {
long msb = uuid.getMostSignificantBits();
long timestampMs = msb >>> 16; // top 48 bits
return Instant.ofEpochMilli(timestampMs);
}
public static void main(String[] args) {
// Anyone with the UUID can extract the creation time
// UUID: 019234ab-cdef-7xxx-xxxx-xxxxxxxxxxxx
// Timestamp: 2026-06-15T10:30:00Z
}
}
This is harmless for an internal identifier but means UUIDv7 must never serve as a secret token.
Why is UUIDv7 not a secret or auth token?
Because part of the value is predictable time and only the tail is random, a UUIDv7 is not unguessable in the cryptographic sense.
package com.bank.security;
import java.security.SecureRandom;
import java.util.Base64;
public class TokenService {
// Wrong: UUIDv7 as a password-reset token
// The timestamp is predictable, reducing entropy
// Correct: fully random, cryptographically secure token
public String generateSecureToken() {
byte[] bytes = new byte[32]; // 256 bits
new SecureRandom().nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
}
When should you use UUIDv7 versus alternatives?
| Use Case | Recommended ID Type |
|---|---|
| Distributed primary key | UUIDv7 (locality + uniqueness) |
| Strict global ordering | Database sequence or logical clock |
| Session or reset token | 256-bit SecureRandom |
| Public API identifier | UUIDv7 (if time leak is acceptable) |
| Legal invoice number | Gapless database sequence |
How do you use UUIDv7 with JPA/Hibernate?
Rajesh maps the UUID as the entity id at National Bank of India.
package com.bank.model;
import jakarta.persistence.*;
import java.math.BigDecimal;
import java.util.UUID;
@Entity
@Table(name = "transactions")
public class Transaction {
@Id
@Column(columnDefinition = "uuid")
private UUID id;
@Column(precision = 18, scale = 2)
private BigDecimal amount;
@PrePersist
void generateId() {
if (id == null) {
// Use a UUIDv7 generator library
// id = UUIDv7.generate();
}
}
}
What are common interview follow-up questions?
- How does UUIDv7 compare to UUIDv4 for index performance? (v7 is time-ordered so inserts are sequential; v4 is random and causes page splits.)
- Can you use UUIDv7 for pagination? (Yes, for rough cursor-based pagination, but not for strict ordering across nodes.)
- What about UUIDv6? (Similar to v7 but reorders the fields of v1; v7 is the modern choice.)
Visual Overview
UUIDv7 structure (128 bits): ┌──────────────────────────┬──────┬──────────────────────┐ │ 48-bit Unix timestamp ms │ ver │ 74-bit random │ │ (creation time: visible) │ (7) │ (entropy) │ └──────────────────────────┴──────┴──────────────────────┘ Caveat 1: Clock skew across nodes Node A: 10:00:00.000 ──▶ UUID_A Node B: 10:00:00.003 ──▶ UUID_B If clocks disagree: UUID_A may sort before UUID_B even if B was first Caveat 2: Storage BINARY(16): 16 bytes ◄── compact CHAR(36): 36 bytes ◄── 2.25x larger, bloats every index Caveat 3: Time leakage UUID ──▶ extract top 48 bits ──▶ creation timestamp revealed Identifier: OK │ Secret/token: NOT OK
Key Takeaway
UUIDv7 is excellent for distributed primary keys because its time prefix gives B-tree locality and avoids the random-insert fragmentation of UUIDv4. But it has three caveats that cause real bugs if ignored.
First, ordering scope: the time prefix comes from each node's wall clock, so within one node the ids are reliably increasing, but across nodes clock skew makes the ordering only approximate. Never use UUIDv7 ordering for strict business sequencing like settlement order.
Second, storage: a UUID is 16 bytes as a native type or BINARY(16), but 36 bytes as CHAR(36). Since the primary key is embedded in every secondary index in InnoDB, the text form bloats the entire index footprint. Store compact, convert to text only at the display edge.
Third, confidentiality: the leading 48 bits are a readable creation timestamp, so a UUIDv7 reveals when a record was created. It is an identifier, never a secret, session token, or password-reset token. For those, use a 256-bit SecureRandom value.
At the National Bank of India, a settlement job once ordered cross-region transactions by their UUIDv7 ids and got the order wrong under clock skew. Rajesh fixed it by ordering on an explicit event-time column and reserving UUIDv7 as a compact, locality-friendly internal key.
Remember one sentence: UUIDv7 is approximately ordered, must be stored as 16-byte binary, and leaks creation time so it is an identifier, never a secret.
---