MySQL Auto Incremented Tables and Upserts

According to best practices, when modeling a MySQL relation that has a UUID as its id, you should still create a hidden numeric auto_increment field for the primary key. Doing so has all sorts of performance advantages (see this post from the founder of Percona or these interesting benchmarks).

However, if you’re used to writing upsert queries with MySQL’s ON DUPLICATE KEY UPDATE... syntax, you’ll soon find that doing so is a recipe for wasting auto increment values.

For example, consider the following create table statement:

CREATE TABLE contrived_examples(
  hidden_id BIGINT AUTO_INCREMENT NOT NULL,
  id BINARY(16) NOT NULL UNIQUE,
  updateable INT NOT NULL,
  PRIMARY KEY (hidden_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Performing an upsert on the table will consume an auto increment value each time, regardless of whether the query only updates a row.

INSERT INTO contrived_examples (id, updateable)
VALUES (0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, 42)
ON DUPLICATE KEY UPDATE updateable=VALUES(updateable);

In the insert case, a record with the specific UUID doesn’t exist, and the query auto increments hidden_id as you would expect. However, when you trigger the duplicate key part of the query during an update, an auto increment value is also consumed because MySQL first tries to perform the update (consuming an id), detects the duplicate, and then performs the update.

This is a simple oversight that is easy to make, but left unchecked, can waste much of your id space. It should also be noted that the same problem can be experienced when using INSERT IGNORE.... To avoid either type of query, you should do the following:

  1. Try to insert the record (INSERT INTO contrived_examples (id, updateable) VALUES (0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, 42))
  2. Check/catch the case that no rows were inserted
  3. If no rows were inserted, update the existing row (UPDATE contrived_examples SET updateable=42 WHERE id=0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)

This issue is why I spent time changing all our fancy upserts into separate update and insert queries shortly after our DevOps Monitoring Platform, Blue Matador, was released. We monitor servers and applications all day every day and it didn’t take long for us to notice a very large gap in the ids that were being generated.

Hopefully this post helps you avoid the same level of tedium.


Monitor your MySQL databases with Watchdog, the forever free server monitor from Blue Matador.

Picture of Mark Siebert

Mark Siebert

Author Bio

Mark is a full-stack engineer who cut his teeth on professional software development at Lucid Software where he specialized in building scalable backend systems. He's a huge fan of open source software and is one of the primary authors of Relate (database access library) and Cumulus (AWS infrastructure management). Mark loves watching shows with his wife, studying Chinese, and hunting Minecraft chickens.

What is Blue Matador?

Blue Matador is the AI-powered DevOps monitoring platform that provides real-time, predictive alerts that help your team decrease downtime and increase customer confidence in your brand. Learn more

Our Monitoring Products

Watchdog is the free server monitor that sends you and your team proactive system vitals alerts, proactively notifying you of all the metrics you need to know to prevent downtime. Install for free


Lumberjack is the AI-powered centralized log management tool that proactively warns your DevOps team of impending server and app issues that affect uptime. Try free for 14 days