[Mysql] ID jumps when using UPSERT

You might have noticed that the PRIMARY KEY ID jumps even when no new record is inserted. This behavior commonly occurs when using the UPSERT operation.

In MySQL, where the ID column is set to AUTO_INCREMENT, the increment key is consumed whenever you perform an UPSERT (INSERT ... ON DUPLICATE KEY UPDATE). This happens because MySQL assigns an incremented key value before checking for conflicts. If a conflict is detected (e.g., a duplicate key), MySQL updates the existing record instead of inserting a new one. However, the incremented key that was assigned is not reused and is effectively “lost.”

This results in gaps in the sequence of the AUTO_INCREMENT values, causing the ID to jump even if no new record was inserted.

Example

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255) UNIQUE,
    email VARCHAR(255)
);

-- repeat this many times
INSERT INTO users (username, email)
VALUES ('john', 'john@example.com')
ON DUPLICATE KEY UPDATE email = VALUES(email);

-- check jumped ID
INSERT INTO users (username, email)
VALUES ('doe', 'doe@example.com')
ON DUPLICATE KEY UPDATE email = VALUES(email);

Explanation:

  • Step 1: You insert a user with the username 'john' and email 'john@example.com'.
  • Step 2: You repeatedly perform an UPSERT on 'john', which results in an update each time since 'john' is a unique key.
  • Step 3: Each UPSERT operation still consumes an AUTO_INCREMENT ID, even though no new record is inserted.
  • Step 4: After multiple UPSERT operations, when you insert a new user (e.g., 'doe'), the ID value will have jumped, reflecting the number of times an UPSERT was attempted.

Why This Happens

MySQL’s AUTO_INCREMENT mechanism reserves the next sequential ID as soon as an INSERT is attempted. Even when the INSERT results in an update due to a duplicate key conflict, the reserved ID is discarded and never reused. This behavior ensures that each ID remains unique and that the AUTO_INCREMENT sequence always increases, even when no new records are added.

How to Handle This

This behavior is normal in MySQL and is by design. While it might seem inefficient in terms of ID usage, it ensures the integrity of the AUTO_INCREMENT mechanism. If avoiding gaps in the ID sequence is crucial for your application, you may need to reconsider the use of UPSERT in favor of explicit SELECT queries followed by INSERT or UPDATE, though this could lead to performance trade-offs.

Leave a Reply

Your email address will not be published. Required fields are marked *