您好我有一个简单的MySQL InnoDB表,只有两个字段:
我正在并行地从各种来源导入一些数据,我需要确保数据在插入时没有重复,所以我正在执行以下操作:
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
&lt; = 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`
OR
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
答案 0 :(得分:1)
你正在遇到竞争条件。当您的代码检测到需要新插入时,您的两个客户端将竞相成为第一个插入该值的客户端。这是赢家通吃。您需要编写代码以避免此竞争条件。幸运的是,SQL是专门设计的,因此可以做到这一点。
这里有几个选择,都是针对MySQL的SQL方言。
一种是使用内置函数LAST_INSERT_ID()
。我认为你的意思是get AUTO_INCREMENT
。
另一种是使用INSERT ... ON DUPLICATE KEY UPDATE
。
看起来你的逻辑意图做两件事:
name
值在表中,如果尚未存在,请将其放在那里。id
值。你可以这样做。
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.