我有这样一个表: 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。
这不是一个很好的解决方案,因为它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列,或您指定的自动递增只踢NULL
或DEFAULT
作为其值。
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?