我使用InnoDB引擎为MySQL数据库v5.7.16实现了一个基于表的自定义序列生成器。
sequence_table
如下所示:
+-------------+-----------+
|sequence_name|next_value |
+-------------+-----------+
| first_seq | 1 |
+-------------+-----------+
| second_seq | 1 |
+-------------+-----------+
sequence_name
列是主键
该序列表包含针对不同消费者的多个序列。
我使用以下策略进行序列更新:
select next_val from sequence_table where sequence_name=?
。 update sequence_table set next_val=? where sequence_name=? and next_val=?
。该文档包含以下信息:
UPDATE ... WHERE ...在每条记录上设置一个独占的下一键锁定 搜索遇到。但是,只需要索引记录锁定 对于使用唯一索引锁定行以搜索a的语句 独特的一排。 14.5.3 Locks Set by Different SQL Statements in InnoDB
粗体部分有点令人困惑
如您所见,我匹配WHERE
语句的UPDATE
子句中的主键。
搜索是否可能遇到多个记录,因此在此序列表中锁定多行?
换句话说,算法第3步中的更新会阻止一行还是多行?
答案 0 :(得分:1)
您没有提到您计划使用的事务隔离级别。
让我们假设你正在使用repeatable read
(在read committed
中不存在这样的问题)
来自here:
对于锁定读取(使用FOR UPDATE或LOCK IN SHARE MODE选择), UPDATE和DELETE语句,锁定取决于是否 statement使用具有唯一搜索条件的唯一索引,或者a 范围类型搜索条件
和
对于具有唯一搜索条件的唯一索引,InnoDB仅锁定 发现的指数记录,而不是之前的差距
因此,至少在理论上它应该只锁定一条记录,并且不会使用下一键锁定。
来自其他文档页面的更多引用支持我的想法:
下一键锁定是索引记录上的记录锁定的组合 并且在索引记录之前对间隙进行间隙锁定。
使用唯一锁定行的语句不需要间隙锁定 用于搜索唯一行的索引
答案 1 :(得分:0)
START TRANSCTION
。autocommit=ON
在单个语句中执行任务。这两个导致它更快,更不容易阻止。
(您的代码缺少BEGIN/COMMIT
和FOR UPDATE
。我摆脱了这些而不是解释问题。)
设置测试:
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)
从'first'获取20个nums并获取起始编号:
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)
再抓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)