1

我有一张像这样的桌子: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。

4

1 回答 1

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

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


回复您的评论:

即使 PK 被声明为自动增量,您仍然可以指定一个值。仅当您在 INSERT 中指定 PK 列,或者您指定NULLorDEFAULT作为其值时,自动增量才会启动。

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 之外工作。

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

于 2013-03-02T19:28:58.043 回答