Reputation: 4410
I'm implementing a custom table-based sequence generator for MySQL database v5.7.16 with InnoDB engine.
The sequence_table
looks as follows:
+-------------+-----------+
|sequence_name|next_value |
+-------------+-----------+
| first_seq | 1 |
+-------------+-----------+
| second_seq | 1 |
+-------------+-----------+
sequence_name
column is a primary key.
This sequence table contains multiple sequences for different consumers.
I use the following strategy for the sequence updates:
select next_val from sequence_table where sequence_name=?
. update sequence_table set next_val=? where sequence_name=? and next_val=?
.The documentation contains the following information:
UPDATE ... WHERE ... sets an exclusive next-key lock on every record the search encounters. However, only an index record lock is required for statements that lock rows using a unique index to search for a unique row. 14.5.3 Locks Set by Different SQL Statements in InnoDB
The part in bold is a bit confusing.
As you can see, I match the primary key in the WHERE
clause of the UPDATE
statement.
Is it possible that the search may encounter more than one record and therefore lock multiple rows in this sequence table?
In other words, will the update in the 3rd step of the algorithm block just one or multiple rows?
Upvotes: 3
Views: 2359
Reputation: 142208
START TRANSCTION
.autocommit=ON
.Both of those lead to it being much faster, less likely to block.
(You code was missing BEGIN/COMMIT
and FOR UPDATE
. I got rid of those rather than explaining the issues.)
Set up test:
mysql> CREATE TABLE so49197964 (
-> name VARCHAR(22) NOT NULL,
-> next_value INT UNSIGNED NOT NULL,
-> PRIMARY KEY (name)
-> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.02 sec)
mysql> INSERT INTO so49197964 (name, next_value)
-> VALUES
-> ('first', 1), ('second', 1);
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM so49197964;
+--------+------------+
| name | next_value |
+--------+------------+
| first | 1 |
| second | 1 |
+--------+------------+
2 rows in set (0.00 sec)
Grab 20 nums from 'first' and fetch the starting number:
mysql> UPDATE so49197964
-> SET next_value = LAST_INSERT_ID(next_value) + 20
-> WHERE name = 'first';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 1 |
+------------------+
1 row in set (0.00 sec)
mysql> SELECT * FROM so49197964;
+--------+------------+
| name | next_value |
+--------+------------+
| first | 21 |
| second | 1 |
+--------+------------+
2 rows in set (0.00 sec)
Grab another 20:
mysql> UPDATE so49197964
-> SET next_value = LAST_INSERT_ID(next_value) + 20
-> WHERE name = 'first';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 21 |
+------------------+
1 row in set (0.00 sec)
mysql> SELECT * FROM so49197964;
+--------+------------+
| name | next_value |
+--------+------------+
| first | 41 |
| second | 1 |
+--------+------------+
2 rows in set (0.00 sec)
Upvotes: 0
Reputation: 11543
You didn't mention what transaction isolation level you're planning to use.
Lets assume you're using repeatable read
(in read committed
no such a problem should exist)
From here:
For locking reads (SELECT with FOR UPDATE or LOCK IN SHARE MODE), UPDATE, and DELETE statements, locking depends on whether the statement uses a unique index with a unique search condition, or a range-type search condition
and
For a unique index with a unique search condition, InnoDB locks only the index record found, not the gap before it
So at least in theory it should lock only a single record and no next-key lock will be used.
More quotes from other docs pages to back my thoughts:
A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record.
Gap locking is not needed for statements that lock rows using a unique index to search for a unique row
Upvotes: 2