How much space does binary storage save?
A UUID contains 128 bits: 16 bytes. Its familiar hyphenated representation contains 36 ASCII characters. Removing the hyphens leaves 32 hexadecimal characters, which still represent the same 16 bytes.
| Representation | Identifier payload |
|---|---|
| Hyphenated ASCII text | 36 bytes |
| ASCII hexadecimal without hyphens | 32 bytes |
| Binary UUID | 16 bytes |
The calculation (36 - 16) / 36 gives a 55.6% payload reduction. Row headers, indexes, encoding, alignment, and other columns affect the actual storage difference. Converting an existing native UUID column to another binary type does not produce this saving.
Smaller keys can improve cache use, but binary storage alone does not guarantee faster queries. The UUID version, index layout, workload, and database engine all matter.
MySQL: keep the conversion convention consistent
For MySQL 8.0 and later, store the identifier in BINARY(16) and convert at the application boundary:
CREATE TABLE users (
id BINARY(16) PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
INSERT INTO users (id, name)
VALUES (UUID_TO_BIN('550e8400-e29b-41d4-a716-446655440000'), 'Alice');
SELECT BIN_TO_UUID(id) AS uuid, name FROM users;The optional swap flag is intended for version 1 timestamp fields. If existing data uses UUID_TO_BIN(value, 1), decode it with BIN_TO_UUID(value, 1). MySQL's UUID() generates version 1 values. Use the default unswapped conversion for v4 and v7; applying the v1 rearrangement to v7 disrupts its timestamp ordering. These conventions are documented in the MySQL UUID conversion reference.
Do not switch the flag for an existing column without migrating and verifying every value. A mismatched flag can produce a different, valid-looking identifier.
PostgreSQL: use the native uuid type
PostgreSQL stores UUIDs in a dedicated 128-bit type. A primary key creates a unique B-tree index by default. This example uses the built-in version 4 generator available in PostgreSQL 13 and later:
CREATE TABLE users (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
name text NOT NULL
);
INSERT INTO users (name) VALUES ('Alice') RETURNING id;You do not need pgcrypto for this function on those versions. PostgreSQL 18 also provides uuidv7() when timestamp ordering suits the workload. Storage accepts UUIDs independently of the generation method. See the UUID type and UUID functions.
MongoDB: agree on the UUID representation
In mongosh, insert and query the same UUID value with the UUID helper:
db.users.insertOne({
_id: UUID("550e8400-e29b-41d4-a716-446655440000"),
name: "Alice"
});
db.users.findOne({
_id: UUID("550e8400-e29b-41d4-a716-446655440000")
});The mongosh UUID helper creates a BSON UUID value. Modern standard UUID storage uses binary subtype 4. Older subtype 3 data can follow driver-specific byte orders, so configure the driver representation explicitly when reading legacy data. See MongoDB's UUID representation guide.
A UUID stored as a string and a UUID stored as BSON binary are different query values. Changing only the query code will not convert existing records.
Python: verify a round trip
import uuid
original = uuid.UUID("550e8400-e29b-41d4-a716-446655440000")
payload = original.bytes
restored = uuid.UUID(bytes=payload)
assert len(payload) == 16
assert restored == original
assert payload.hex() == "550e8400e29b41d4a716446655440000"
print(str(restored))Python's bytes and bytes_le properties use different field byte orders. Choose the representation your database and other clients expect; do not interchange them. The Python UUID reference specifies both layouts.
Node.js: use the uuid package's ESM API
Install the uuid package, then save this example as an .mjs file:
import { parse, stringify } from 'uuid';
const original = '550e8400-e29b-41d4-a716-446655440000';
const payload = Buffer.from(parse(original));
const restored = stringify(payload);
if (payload.length !== 16 || restored !== original) {
throw new Error('UUID round trip failed');
}
console.log(restored);The uuid package documents parse and stringify. Current releases use ESM imports. Preserve all 16 bytes, including leading zero bytes; treating the value as a JavaScript Number loses precision.
Measure before migrating
Compare the same dataset and queries under each proposed schema. Record table and index sizes, insert throughput, and lookup and join latency under representative concurrency. Run enough repetitions to distinguish a consistent effect from cache warm-up or background activity.
Before switching production reads, backfill a separate column, verify conversion round trips and foreign-key relationships, and check that every client uses the same byte order. Keep the existing representation available until rollback and mixed-client behavior have been tested.
Use the UUID to binary converter to inspect a value, or read about UUID-based sharding for the separate question of distributing records across nodes.
Check your implementation
Use the UUID examples and test vectors to verify versions, v7 timestamps and canonical byte order. Download the fixed fixtures for repeatable tests; generate fresh identifiers for production records.
