I've got a MySql table with an auto-increment primary key, and it seems that all of the various upsert methods (INSERT IGNORE and ON DUPLICATE KEY UPDATE) suffer from the, uh, feature that the auto-increment field increments, even if a row is updated and not inserted. This means that gaps are introduced into the table, which I find undesirable.
So the question is: is there any way to upsert records in a table with an auto-increment field without auto-incrementing that field, if the upsert in fact merely updates the row. To my mind, this is the way upsert should behave, but it doesn't seem to.
This "problem" is only in InnoDB.
It is by design, and intended to improve concurrency: another thread can use an AUTO_INCREMENT without having to wait for the results of an UPSERT operation.
From the docs:
After a server startup, for the first insert into a table
t,InnoDBexecutes the equivalent of this statement:SELECT MAX(ai_col) FROM t FOR UPDATE;…
InnoDBinitializes but does not increment the value and stores it for use by later inserts…
When accessing the auto-increment counter,
InnoDBuses a special table-levelAUTO-INClock that it keeps to the end of the currentSQLstatement, not to the end of the transaction. The special lock release strategy was introduced to improve concurrency for inserts into a table containing anAUTO_INCREMENTcolumn. Nevertheless, two transactions cannot have theAUTO-INClock on the same table simultaneously, which can have a performance impact if theAUTO-INClock is held for a long time. That might be the case for a statement such asINSERT INTO t1 ... SELECT ... FROM t2that inserts all rows from one table into another.
MyISAM does not exhibit this behavior, since it's AUTO_INCREMENT algorithm is implemented differently (due to its limited ability to support concurrent DML).
来源:https://stackoverflow.com/questions/3679611/mysql-upsert-and-auto-increment-causes-gaps