Skip to main content

Searching...

Tools
Articles
View All Results

Developer Lab · MySQL

Generate UUID in MySQL

MySQL has built-in UUID support since 4.1.2. UUID() generates a v1 UUID. MySQL 8.0+ adds UUID_TO_BIN() for efficient BINARY(16) storage and BIN_TO_UUID() for retrieval.

Quick Reference

Function Version MySQL Use Case
UUID() v1 5.x+ Quick generation - returns VARCHAR(36)
UUID_TO_BIN(UUID(), 1) v1 (reordered) 8.0+ Efficient storage - BINARY(16), time-bit reordered
BIN_TO_UUID(id, 1) any 8.0+ Convert BINARY(16) back to UUID string
DEFAULT (UUID_TO_BIN(UUID(), 1)) v1 8.0+ Auto-generate UUID on INSERT

Primary Implementation

Production Ready
sql
-- ── Generate a UUID ──────────────────────────────────────────
SELECT UUID();
-- → 6ccd780c-baba-1026-9564-0040f4311e29  (v1, VARCHAR)

-- ── Best practice: store as BINARY(16) ───────────────────────
-- UUID_TO_BIN(uuid, 1) reorders the time bits for better index locality
-- The second argument (1) enables "swap_flag" - moves the time-high bits
-- to the front, making sequential UUIDs sort correctly in B-tree indexes

CREATE TABLE orders (
    id         BINARY(16)   NOT NULL DEFAULT (UUID_TO_BIN(UUID(), 1)),
    customer   VARCHAR(100) NOT NULL,
    total      DECIMAL(10,2) NOT NULL,
    created_at TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id)
);

-- ── Insert without specifying ID (auto-generated) ────────────
INSERT INTO orders (customer, total) VALUES ('Alice', 99.99);

-- ── Insert with explicit UUID ─────────────────────────────────
INSERT INTO orders (id, customer, total)
VALUES (UUID_TO_BIN(UUID(), 1), 'Bob', 149.99);

-- ── Query - convert BINARY(16) back to UUID string ───────────
SELECT BIN_TO_UUID(id, 1) AS id, customer, total
FROM orders;

-- ── Find by UUID string ───────────────────────────────────────
SELECT BIN_TO_UUID(id, 1) AS id, customer
FROM orders
WHERE id = UUID_TO_BIN('6ccd780c-baba-1026-9564-0040f4311e29', 1);

All Approaches

Simple VARCHAR(36) - MySQL 5.x compatible

sql
-- Simple approach - works on MySQL 5.x
-- Downside: VARCHAR(36) uses 36 bytes vs 16 bytes for BINARY(16)
-- Also causes B-tree index fragmentation with random UUIDs

CREATE TABLE users (
    id         VARCHAR(36)  NOT NULL DEFAULT (UUID()),
    name       VARCHAR(100) NOT NULL,
    email      VARCHAR(255) NOT NULL UNIQUE,
    created_at TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id)
);

INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');

SELECT id, name FROM users;

BINARY(16) with swap_flag - MySQL 8.0+ recommended

sql
-- BINARY(16) with swap_flag=1 - best for high-insert-rate tables
-- swap_flag reorders time bits so sequential UUIDs are adjacent in the index
-- This eliminates B-tree page splits and dramatically improves INSERT performance

CREATE TABLE events (
    id         BINARY(16)   NOT NULL DEFAULT (UUID_TO_BIN(UUID(), 1)),
    event_type VARCHAR(50)  NOT NULL,
    payload    JSON,
    created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
    PRIMARY KEY (id),
    INDEX idx_created (created_at)
);

-- Generate UUID in application and pass it in
-- (recommended for idempotent inserts)
INSERT INTO events (id, event_type, payload)
VALUES (UUID_TO_BIN('6ccd780c-baba-1026-9564-0040f4311e29', 1), 'order.created', '{"orderId": 42}');

-- Read back as UUID string
SELECT BIN_TO_UUID(id, 1) AS id, event_type, created_at
FROM events
ORDER BY created_at DESC
LIMIT 10;

Stored procedure - generate and return UUID

sql
DELIMITER $$

CREATE PROCEDURE create_order(
    IN  p_customer VARCHAR(100),
    IN  p_total    DECIMAL(10,2),
    OUT p_id       VARCHAR(36)
)
BEGIN
    SET p_id = UUID();
    INSERT INTO orders (id, customer, total)
    VALUES (UUID_TO_BIN(p_id, 1), p_customer, p_total);
END$$

DELIMITER ;

-- Call the procedure
CALL create_order('Alice', 99.99, @order_id);
SELECT @order_id AS created_order_id;

Common Mistakes

Storing UUIDs as VARCHAR(36) in high-traffic tables

VARCHAR(36) uses 36 bytes per row vs 16 bytes for BINARY(16). More importantly, random UUIDs as primary keys cause B-tree index fragmentation - every INSERT goes to a random page, causing constant page splits and poor cache utilization. Use BINARY(16) with UUID_TO_BIN(UUID(), 1).

Forgetting the swap_flag argument in UUID_TO_BIN()

UUID_TO_BIN(UUID()) without the second argument stores the UUID as-is - random bytes, no time ordering. Use UUID_TO_BIN(UUID(), 1) to reorder the time bits for sequential inserts.

Using UUID() in WHERE clauses without UUID_TO_BIN()

If your id column is BINARY(16), you must convert the string in your WHERE clause: WHERE id = UUID_TO_BIN('...', 1). Comparing a BINARY(16) column to a plain string will not use the index.

How It Works

MySQL's UUID() generates a version 1 UUID - it encodes the current timestamp (100-nanosecond intervals since Oct 15, 1582) plus a random node ID. It is unique across time and space but leaks timing information.

UUID_TO_BIN(uuid, 1) converts the 36-char string to 16 bytes and reorders the time components so that UUIDs generated close together are stored adjacent in the B-tree index - dramatically improving INSERT performance on large tables.

Output Formats

UUID()

6ccd780c-baba-1026-9564-0040f4311e29

UUID_TO_BIN(UUID(), 1)

BINARY(16) - 16 bytes

BIN_TO_UUID(id, 1)

6ccd780c-baba-1026-9564-0040f4311e29

Best Practices, Performance, and Security

Best practices

Use BINARY(16) with UUID_TO_BIN(UUID(), 1) for production tables.

Generate UUIDs in your application layer for idempotent inserts - pass them to MySQL rather than relying on DEFAULT (UUID()).

Always use BIN_TO_UUID(id, 1) in SELECT queries to return human-readable UUIDs.

Performance

BINARY(16) with swap_flag=1 gives near-sequential insert performance - comparable to auto-increment integers. Without the swap flag, random UUIDs cause severe B-tree fragmentation on tables with millions of rows.

Index size: BINARY(16) = 16 bytes vs VARCHAR(36) = 36 bytes - 55% smaller indexes, better cache utilization.

Security

MySQL's UUID() generates a v1 UUID - it encodes the server's MAC address and timestamp. This leaks server identity and timing information.

For security-sensitive IDs (session tokens, API keys), generate UUID v4 in your application layer using a CSPRNG and pass it to MySQL - don't rely on UUID().

Installation

Built-in functions

UUID()MySQL 5.x+
UUID_TO_BIN()MySQL 8.0+
BIN_TO_UUID()MySQL 8.0+
DEFAULT (UUID_TO_BIN(...))MySQL 8.0.13+

Frequently Asked Questions

How do I generate a UUID in MySQL?

MySQL's built-in UUID() function returns a version 1 (time- and MAC-address-based) UUID, not a random v4. It has been available since MySQL 4.1.2. For index-friendly storage on MySQL 8.0+, wrap it with UUID_TO_BIN(UUID(), 1) and store the result in BINARY(16). MySQL has no native v4 or v7 generator, so produce those in your application layer.

Is UUID() cryptographically secure?

No. MySQL's UUID() returns a version 1 UUID derived from the timestamp and the server's MAC address, not from a CSPRNG, so it is predictable and leaks server identity. It is not suitable for session tokens, API keys, or other security-sensitive identifiers - generate a v4 UUID with a CSPRNG in your application layer for those.

What is the difference between UUID v4 and v7 in MySQL?

Note that MySQL's UUID() is v1 (time + MAC), not v4. A v4 UUID is fully random and not sortable, while v7 embeds a millisecond timestamp for chronological sorting (RFC 9562). MySQL has no native v4 or v7 function, so generate those in your application and store them as BINARY(16); use v7 for database primary keys at scale.

Do I need to install a package for UUID generation in MySQL?

No package is needed for MySQL's built-in UUID(), which returns a v1 UUID. MySQL cannot generate v4 or v7 natively, so for those you generate the UUID in your application layer and insert it.

How do I validate a UUID string in MySQL?

Use the platform's UUID parse/validation function, or test against the RFC 4122 regex: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i. Always validate external input at API boundaries.

Should I use UUIDs as database primary keys in MySQL applications?

UUIDs work well as primary keys for distributed systems. Prefer native UUID/BINARY(16) column types over VARCHAR(36). For very large tables, consider UUID v7 for better B-tree insert locality.

Can I generate UUIDs in MySQL without a network connection?

Yes. UUID generation uses local OS entropy sources and does not require network access. Each call is independent and thread-safe on modern platforms.

What output formats are available in MySQL?

The standard hyphenated lowercase string (36 chars) is the default. Most APIs also support 32-char hex (no hyphens) and 16-byte binary formats. Use string format for APIs and binary for database storage.

What RFC standards apply to MySQL UUID generation?

Version 4 UUIDs follow RFC 4122. UUID v7 follows RFC 9562 (May 2024). Ensure your chosen method produces compliant version and variant bits.

When should I avoid UUID v1?

Avoid UUID v1 in security-sensitive contexts - it embeds the server MAC address and timestamp. Note that MySQL's UUID() is itself v1, so when you need unpredictability, generate a v4 in your application layer instead of relying on UUID().

Key definitions

UUID
128-bit universally unique identifier, usually shown as 36 hex characters with hyphens.
CSPRNG
Cryptographically secure pseudo-random number generator - the entropy source behind secure UUID generation.
RFC 4122
IETF standard defining UUID versions 1 through 5. Version 4 is random.
RFC 9562
IETF standard adding UUID versions 6, 7, and 8. Version 7 is time-ordered.