UUIDs let systems generate keys independently, while integers offer compact storage and increasing allocation. Choosing between them depends on your workload.

If you expose a UUID, keep authorization checks and consider the information its version encodes, including the timestamp in v7.

What integers do well

Size. A bigint is 8 bytes. A UUID is 16. Every index that references the key, every foreign key column, every join buffer pays that cost twice over.

Insert locality. Increasing allocated values can group inserts in an index. Concurrent writers, gaps and custom values mean this is not a guarantee of insertion order.

Readability. Order 1042 is easy to say on a support call. Order 51a41b63-8654-46d4-af7a-c75bbfc510a9 is not.

What UUIDs do well

Generation anywhere. A mobile app can create a record with its ID before it has any connection, then sync later without a central ID allocator. Keep a uniqueness constraint to catch duplicates. Two databases can be merged without renumbering.

No information leak. A sequential ID in a URL tells competitors how many customers you have and lets anyone enumerate your records by adding one. A v4 UUID does not encode a sequence or timestamp. A v7 UUID reveals its timestamp. Neither replaces access control.

No round trip. With integers the application must insert and then read back the generated ID. With UUIDs the application already knows the ID and can use it in related inserts immediately.

The fragmentation problem with v4

A B-tree keeps keys in sorted order.

Random v4 keys send successive inserts to different positions, which can reduce cache locality and cause page splits. RFC 9562 describes this locality problem.

The effect depends on table size, cache capacity and workload.

Compare insert throughput and index size on your own PostgreSQL or MySQL data.

InnoDB stores table rows in the clustered index, usually the primary key. Its index documentation explains why primary key choices also affect secondary indexes.

How v7 changes insert locality

UUID v7 puts a millisecond timestamp in the first 48 bits.

Keys group by timestamp instead of scattering across the full key space, which can improve locality. Same millisecond order, clock changes and concurrent writers depend on the generator.

The RFC explains the ordering choices.

You still store a 16 byte key; v7 does not make it the size of a bigint. Read UUID v4 vs v7 for the layout.

Store it as a real UUID type

Whatever you choose, do not store UUIDs as 36 character strings. That is 36 bytes plus overhead instead of 16, and string comparison is slower than binary.

PostgreSQL has a native uuid column type. Use gen_random_uuid() for v4 or, on version 18 and later, uuidv7() as the default.

MySQL has no UUID type. Use BINARY(16) and convert with UUID_TO_BIN() and BIN_TO_UUID(). Generate v7 in the application, since the built in UUID() function returns v1.

SQL Server has uniqueidentifier. Be aware it sorts GUIDs by an unusual byte order, so v7 ordering is not preserved without care.

SQLite stores them as a 16 byte BLOB.

A hybrid pattern

Some teams use both: an internal bigint primary key for joins and foreign keys, plus a UUID column with a unique index for anything exposed to the outside world.

This gets the smallest indexes and the safest URLs at the cost of one extra column and one extra lookup on external requests. It is a good fit for very large tables with many foreign key references.

Recommendation

Small to medium tables, or any table where rows are created outside the database: UUID v7 as the primary key.

Very large tables with heavy joins and no external exposure: bigint, or the hybrid pattern.

Existing tables with v4 keys that are performing fine: leave them. Switching primary keys is expensive and the gain only matters at scale.

To see what v7 keys look like in practice, generate a batch with our UUID generator and paste them into a test table sorted by primary key.