InnoDB:使用插入选择自定义自动增量。可能存在重复键错误吗?

时间:2013-03-02 19:05:17

标签: mysql innodb percona xtradb

我有一张像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。

1 个答案:

答案 0 :(得分:2)

这不是一个好的解决方案,因为它在执行SELECT时会在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         

这就是自动增量机制以它的方式工作的原因。无论事务隔离如何,插入线程仅将表暂时锁定以增加auto-inc编号。这非常快。然后释放锁定,允许其他线程立即继续。同时,第一个线程试图完成其插入。

有关自动增加锁定的详细信息,请参阅http://dev.mysql.com/doc/refman/5.5/en/innodb-auto-increment-handling.html

我不确定为什么要模拟自动增量行为,而不是仅将列定义为自动增量列。 You can change an existing table to be auto-incrementing.


重新评论:

即使PK被声明为自动增量,您仍然可以指定一个值。如果在IN​​SERT中指定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锁。

您真的不应该尝试重新发明AUTO-INCREMENT的行为,因为任何SQL解决方案都受ACID属性的限制。 Auto-inc必须在ACID之外工作。

如果您需要以原子方式更正现有行,请使用either REPLACE or INSERT...ON DUPLICATE KEY UPDATE