INT AUTO_INCREMENT PRIMARY KEY
and UNIQUE KEY
.INSERT ... ON DUPLICATE KEY UPDATE
(hereafter, the INSERT ODKU
), where the columns listed in the INSERT
corresponded to the columns with the UNIQUE KEY
. And they were executed with a frequency of approximately 1500-2000 requests per second, continuously 24 hours a day. If you are good at mathematics, then probably guessed what was the matter. CREATE TABLE update_test ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, username VARCHAR(20) NOT NULL, host_id TINYINT UNSIGNED NOT NULL, last_modified TIMESTAMP NULL DEFAULT NULL, PRIMARY KEY(id), UNIQUE KEY(username) ) ENGINE=InnoDB;
(root@localhost) [test]> INSERT INTO update_test (username, host_id, last_modified) VALUES ('foo',3,NOW()); Query OK, 1 row affected (0.00 sec) (root@localhost) [test]> select * from update_test; +----+----------+---------+---------------------+ | id | username | host_id | last_modified | +----+----------+---------+---------------------+ | 1 | foo | 3 | 2012-10-05 22:36:30 | +----+----------+---------+---------------------+
SHOW CREATE TABLE
, we will see that the AUTO_INCREMENT
counter now has the value 2
. If we make an INSERT ODKU
into this table, we will see the following: (root@localhost) [test]> insert into update_test (username,host_id) values ('foo',1) on duplicate key update last_modified=NOW(); Query OK, 2 rows affected (0.00 sec) (root@localhost) [test]> select * from update_test; +----+----------+---------+---------------------+ | id | username | host_id | last_modified | +----+----------+---------+---------------------+ | 1 | foo | 3 | 2012-10-05 22:58:28 | +----+----------+---------+---------------------+ 1 row in set (0.00 sec)
AUTO_INCREMENT
counter has grown to 3
. This is, in fact, the expected behavior. InnoDB checks the constraints in the order in which they were defined, and PRIMARY KEY
always comes first. Therefore, MySQL checks our INSERT
, sees that the next AUTO_INCREMENT
value is available and uses it, but then, it checks the UNIQUE KEY
and finds a violation, so instead of INSERT
it does UPDATE
. If we look at the handler status
counters, we can see that there was one insert request that failed, and one update request that was successful (this explains why the 2 rows are changed, not 1). (root@localhost) [test]> show status like 'handler%'; *** some rows omitted *** +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Handler_commit | 1 | | Handler_rollback | 0 | | Handler_update | 1 | | Handler_write | 1 | +----------------------------+-------+
INSERT ODKU
per second, continuously 24 hours a day. PRIMARY KEY
their tables is the same as I used in the demo table - INT UNSIGNED
. We consider. The maximum value for INT UNSIGNED
is 4294967295
. We divide it by 1500 requests per second and divide by 86400, which is the number of seconds in a day, and we get 33.1 days, or a little more than 4 weeks. Coincidence? I do not think so. So what exactly happens when we go beyond the limits? Some behavior may surprise you. Let's go back to our demo table and insert a row with the maximum value for a column with AUTO_INCREMENT
, and then insert another one. (root@localhost) [test]> insert into update_test (id,username,host_id) values (4294967295, 'bar', 10); Query OK, 1 row affected (0.00 sec) (root@localhost) [test]> flush status; (root@localhost) [test]> insert into update_test (username,host_id) values ('baz', 10); ERROR 1062 (23000): Duplicate entry '4294967295' for key 'PRIMARY' (root@localhost) [test]> show status like 'handler%'; *** some rows omitted *** +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Handler_rollback | 1 | | Handler_write | 1 | +----------------------------+-------+
AUTO_INCREMENT
already had the maximum value and the request did not pass. But, what happens if we try to do an INSERT ODKU
? First, let's see what we have in the table: (root@localhost) [test]> select * from update_test; +------------+----------+---------+---------------------+ | id | username | host_id | last_modified | +------------+----------+---------+---------------------+ | 1 | foo | 3 | 2012-10-05 22:58:28 | | 4294967295 | bar | 10 | NULL | +------------+----------+---------+---------------------+ 2 rows in set (0.00 sec) (root@localhost) [test]> INSERT INTO update_test (username, host_id) VALUES ('foo', 7) ON DUPLICATE KEY UPDATE host_id=7, last_modified=NOW(); Query OK, 2 rows affected (0.00 sec)
username = "foo"
, host_id
and last_modified
were updated, and we can rejoice. Unfortunately, this is not the case: (root@localhost) [test]> select * from update_test; +------------+----------+---------+---------------------+ | id | username | host_id | last_modified | +------------+----------+---------+---------------------+ | 1 | foo | 3 | 2012-10-05 22:58:28 | | 4294967295 | bar | 7 | 2012-10-05 23:24:49 | +------------+----------+---------+---------------------+ 2 rows in set (0.00 sec)
id
is equal to the maximum value of our AUTO_INCREMENT
, and the UNIQUE KEY
on the username
column was ignored.AUTO_INCREMENT
data type of the AUTO_INCREMENT
c INT
to BIGINT
.INSERT ODKU
on a table with several unique indexes will be equivalent to the UPDATE update_test SET host_id = 7, last_modified = NOW() WHERE id = 4294967295 OR username = "foo" LIMIT 1
request UPDATE update_test SET host_id = 7, last_modified = NOW() WHERE id = 4294967295 OR username = "foo" LIMIT 1
and of course the optimizer will choose PRIMARY
rather than secondary UNIQUE
.AUTO_INCREMENT
much easier than it seems. The real customer table contained less than 500k rows.SIGNED
types for AUTO_INCREMENT
is almost always a bad idea. You lose half the range of available values.Source: https://habr.com/ru/post/156489/
All Articles