你好,我有一个简单的MySQL InnoDB表,只有两个字段:
- id-自动递增主索引
- name-唯一索引
我正在并行地从各种来源导入一些数据,我需要确保数据在插入时不会重复,所以我正在做以下操作:
SELECT `id` FROM `table` WHERE `name` = <name>;
if `id` <= 0
INSERT INTO `table` SET `name` = "<name>";
return AUTO_INCREMENT
else return `id`
这在99.9999%的情况下都有效,但两个或多个不同的脚本插入相同的数据可能会发生(在我身上也发生过),因为SELECT都返回id
<0,所以两者都执行INSERT操作,其中一个操作会引发错误。
我心中有两种可能的解决方案,但我不确定哪种方案最有效。
还有一条信息:最初导入不会在表中找到元素,但随着插入更多元素,找到的概率会增加。经过粗略计算,最终的表格将有大约7-10亿条记录:
SELECT `id` FROM `table` WHERE `name` = <name>;
if `id` <= 0
INSERT IGNORE INTO `table` SET `name` = "<name>";
get AUTO_INCREMENT
if AUTO_INCREMENT <=0
SELECT `id` FROM `table` WHERE `name` = <name>;
return `id`
else return AUTO_INCREMENT
else return `id`
或
INSERT IGNORE INTO `table` SET `name` = "<name>";
get AUTO_INCREMENT
if AUTO_INCREMENT <=0
SELECT `id` FROM `table` WHERE `name` = <name>;
return `id`
else return AUTO_INCREMENT
您正处于比赛状态。当您的代码检测到需要进行新的插入时,那么您的两个客户端将争先恐后地成为第一个插入该值的客户端。这是赢家通吃。您需要编写代码来避免这种竞争条件。幸运的是,SQL是专门设计的,所以可以做到这一点。
这里有几个选择,都是MySQL的SQL方言特有的。
一种是使用内置函数LAST_INSERT_ID()
。它达到了我相信你所说的get AUTO_INCREMENT
的目的。
另一种是使用INSERT ... ON DUPLICATE KEY UPDATE
。
看起来您的逻辑旨在做两件事:
- 确保
name
值在表中,如果还没有,就把它放在那里 - 返回与名称值关联的CCD_ 6值
你可以这样做。
INSERT IGNORE INTO `table` (name) VALUES (<name>);
SELECT id FROM `table` WHERE name = <name>;
请注意,INSERT IGNORE
操作不会被访问数据库的不同程序之间的竞争条件所捕获,因为它是一条SQL语句。
您可以使用LAST_INSERT_ID()
对此进行优化。
INSERT IGNORE INTO `table` (name) VALUES (<name>);
if (LAST_INSERT_ID()=0) then do the select.