InnoDB的:使用插入选择自定义自动递增。 不可能有重复键错误?(InnoDB: custom

2019-10-17 18:52发布

我有这样一个表: idx (PK) clmn_1

两者都是在INT。 idx不被定义为自动递增,但我想模拟它。 要插入到这个表中,我使用:

"INSERT INTO  my_tbl (idx, clmn_1)   \
 SELECT IFNULL(MAX(idx), 0) + 1, %s  \
 FROM my_tbl", val_clmn_1

现在,这个工程。 我有查询是关于原子。 因为我读,然后插入到同一个表,多个插入时发生的同时能有可能是重复键错误?

而且,我怎么可以测试它自己?

我使用的Percona XtraDB服务器5.5。

Answer 1:

这不是一个很好的解决方案,因为它MY_TBL创建一个共享锁,而它做的选择。 任何数量的线程可以有一个共享锁同时地,但它的块并行写锁。 因此,这将导致插入到形成系列化,等待SELECT完成。

你可以观察这个锁。 在一个会话启动此查询:

INSERT INTO  my_tbl (idx, clmn_1) 
 SELECT IFNULL(MAX(idx), 0) + 1, 1234+SLEEP(60) 
 FROM my_tbl;

然后去另一个会话和运行innotop并查看锁定屏幕(按键“L”)。 你会看到类似这样的输出:

___________________________________ InnoDB Locks ___________________________________
ID  Type    Waiting  Wait   Active  Mode  DB    Table   Index    Ins Intent  Special
61  TABLE         0  00:00   00:00  IS    test  my_tbl                    0         
61  RECORD        0  00:00   00:00  S     test  my_tbl  PRIMARY           0         

这就是为什么自动递增机制的工作方式是这样。 不管事务隔离的,插入线程简要只锁定表递增自动INC数。 这是非常快速的。 然后释放锁,允许其他线程立即着手。 同时,第一个线程试图完成其插入。

见http://dev.mysql.com/doc/refman/5.5/en/innodb-auto-increment-handling.html有关自动递增锁定的更多细节。

我不知道为什么要模拟自动递增的行为,而不是仅仅定义列的自动增量列。 您可以更改现有的表是自动递增。


回复您的评论:

即使PK被声明为自动递增,你仍然可以指定一个值。 如果你指定在INSERT的PK列,或您指定的自动递增只踢NULLDEFAULT作为其值。

CREATE TABLE foo (id INT AUTO_INCREMENT PRIMARY KEY, c CHAR(1));
INSERT INTO foo (id, c) VALUES (123, 'x'); -- inserts value 123
INSERT INTO foo (id, c) VALUES (DEFAULT, 'y'); -- inserts value 124
INSERT INTO foo (id, c) VALUES (42, 'n'); -- inserts specified value 42
INSERT INTO foo (c) VALUES ('Z'); -- inserts value 125
REPLACE INTO foo (id, c) VALUES (125, 'z'); -- changes existing row with id=125

回复您的评论:

START TRANSACTION; 
SELECT IFNULL(MAX(idx), 0)+1 FROM my_tbl FOR UPDATE; 
INSERT INTO my_tbl (idx, clmn_1) VALUES (new_idx_val, some_val); 
COMMIT; 

这实际上是比你的第一个想法更糟,因为现在的SELECT...FOR UPDATE将创建一个X锁,而不是一个S锁。

你真的应该不要尝试重新发明自动递增的行为,因为任何SQL解决方案是通过ACID特性的限制。 自动INC一定工作ACID之外。

如果你需要原子纠正现有行,使用更换或插入...对重复密钥更新 。



文章来源: InnoDB: custom auto-increment using insert select. Can there be duplicate-key error?