iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

AUTO_INCREMENT

MySQL AUTO_INCREMENT: surrogate keys, BIGINT vs INT, gaps, sequence resets, and the patterns that scale.

MySQL — AUTO_INCREMENT

EXAMPLE
-- ===== Basic =====
CREATE TABLE users (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  email VARCHAR(254) UNIQUE NOT NULL
) ENGINE=InnoDB;

INSERT INTO users (email) VALUES ('a@x.io');
-- id auto-assigned (1)

-- ===== Choose BIGINT for new tables =====
-- INT UNSIGNED maxes at ~4.3 billion. BIGINT UNSIGNED at 18 quintillion.
-- BIGINT is 8 bytes vs INT's 4 — for a primary key, it is worth the extra bytes.

-- ===== Reading the last-inserted ID =====
INSERT INTO users (email) VALUES ('b@x.io');
SELECT LAST_INSERT_ID();        -- 2 (per-session)

-- In Node mysql2:
const [r] = await conn.execute('INSERT INTO users (email) VALUES (?)', [email]);
console.log(r.insertId);

-- ===== Setting / resetting =====
ALTER TABLE users AUTO_INCREMENT = 1000;
-- The next insert uses id = 1000 (or the next free above that).
-- You CANNOT set it lower than the current MAX(id); the engine bumps it.

-- ===== Gaps are normal =====
-- AUTO_INCREMENT is not gap-free:
-- - ROLLBACK consumed an id
-- - REPLACE INTO consumed an id
-- - server crash + restart can skip
-- - InnoDB caches a range per connection (innodb_autoinc_lock_mode)
-- Build apps that DO NOT depend on contiguous ids.

-- ===== Multi-master / replication =====
-- For multi-write topologies, configure offsets to avoid collisions:
-- auto_increment_increment = N
-- auto_increment_offset = M
-- Or use UUIDs / Snowflake IDs.

-- ===== UUID / Snowflake alternatives =====
-- UUID v4 stored as BINARY(16) for compact + indexable:
CREATE TABLE orders (
  id BINARY(16) PRIMARY KEY DEFAULT (UUID_TO_BIN(UUID(), TRUE)),
  ...
);
-- Pros: distributed-friendly, no central authority.
-- Cons: larger keys, slightly less sequential locality.

-- Snowflake / ULID:
-- 64-bit time-ordered IDs; great for distributed systems.

-- ===== Composite keys =====
-- For many-to-many tables, prefer composite primary keys over a surrogate id:
CREATE TABLE post_tags (
  post_id BIGINT UNSIGNED NOT NULL,
  tag_id  BIGINT UNSIGNED NOT NULL,
  PRIMARY KEY (post_id, tag_id),
  FOREIGN KEY (post_id) REFERENCES posts(id),
  FOREIGN KEY (tag_id) REFERENCES tags(id)
) ENGINE=InnoDB;

-- ===== Inspecting + diagnostics =====
SHOW CREATE TABLE users;
SELECT MAX(id), COUNT(*) FROM users;

-- ===== Patterns to internalise =====
-- - BIGINT UNSIGNED AUTO_INCREMENT for almost every new table
-- - LAST_INSERT_ID per session for read-after-insert
-- - Tolerate gaps; never use AUTO_INCREMENT for business identifiers
-- - UUID / Snowflake when you need distributed-friendly IDs

-- ===== Pitfalls =====
-- - INT INT UNSIGNED running out at 4.3B (and you suddenly can not insert)
-- - Depending on consecutive IDs (gaps will appear)
-- - Resetting AUTO_INCREMENT to a value already used (engine bumps anyway)
-- - Using AUTO_INCREMENT in a multi-write topology without offsets

Why it matters

BIGINT UNSIGNED AUTO_INCREMENT is the safe default for new tables. Accept gaps, do not treat the surrogate as a business identifier, and reach for UUID/Snowflake when distributed-friendly IDs become necessary. INT eventually runs out — pick the bigger type once and never think about it again.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
CREATE TABLE orders (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    total DECIMAL(10,2)
);
ALTER TABLE orders AUTO_INCREMENT = 1000;
Try it Yourself »

Exercise

Auto-increment column keyword.

id BIGINT PRIMARY KEY

Discussion

Loading…