mysql插入如果不存在,并发会话

时间:2010-12-11 16:14:06

标签: mysql concurrency insert

我想在用户表中插入新用户,并确保用户的昵称和电子邮件不在表格中(InnoDB)。

这是我的逻辑:

if (SELECT COUNT(*) FROM users WHERE nick = :nick) > 0:
    return "name exists";

if (SELECT COUNT(*) FROM users WHERE email = :email) > 0:
    return "email exists";

# OK to insert? Or something bad can happen here?

INSERT INTO users (nick, email) VALUES (:nick, :email)

但现在我不确定这是不是正确的方法。假设在SELECT和INSERT之间查询其他一些并发连接会创建具有相同缺口或电子邮件的新记录(这是否可能?)。然后INSERT将抛出异常,我无法向前端提供任何反馈(除了简单的“发生错误,再试一次)。

另一个想法是使用INSERT IGNORE然后检查LAST_INSERT_ID(),但是在跳过插入时我是否总是可以确定LAST_INSERT_ID()== 0?

有没有更好的方法来解决这个问题?

4 个答案:

答案 0 :(得分:3)

为什么不使用UNIQUE INDEX?只需插入新值,并在违反唯一约束时让查询失败。一点错误处理就可以解决问题。

UNIQUE约束也将解决您与并发用户的问题。

答案 1 :(得分:1)

INSERT INTO users (nick, email)
SELECT :nick, :email
FROM Dual
WHERE NOT EXISTS (
    SELECT 1
    FROM users
    WHERE nick = :nick OR email =  :email
)

大多数MySql连接器都有办法让行受到影响,或者你可以SELECT ROW_COUNT()

答案 2 :(得分:0)

好问题。

不幸的是,mysql不支持“插入即使不存在”之类的内容。

有几个难看的解决方案。

最好的方法是在您的应用程序中处理它。选择之前,看看你是否得到任何东西,只有你没有得到任何东西时插入。

然后您可以在字段上放置一个唯一键以确保数据库保持一致。

您也可以直接插入并依赖唯一键。您将收到一个错误,您必须在申请中处理。您可以区分错误,以便显示正确的消息。如果我记得那个重复的话,重复的密钥将是1062。

然而,有其他方法可以实现这一目标。

我所知道的一个叫做互斥表。

它的工作原理是你创建了第二个表“mutex”,其中syme键字段作为你的工作表,我现在称之为“table”。 然后你可以做类似的事情:

isnert into table(field1,field2)select(“input1”,“input2”)来自互斥锁左外连接表(mutex.field1 = table.field1 AND mutex.field2 = table.field2)其中mutex.field1! =“input1”AND mutex.field2!=“field2”

我没有测试这个,我希望我能正确记住tequnique,更好的查找它!

也可以将其提升到灵活性,因此您可以在一个表格中仅允许所需数量的重复项。

但这并不能保证数据的一致性,因为数据表也可以访问,所以我真的建议第一种方法:尽可能依赖关键约束,并在应用程序中处理错误号,如果不可能的话,在你的申请中处理它。

答案 3 :(得分:0)

基于以下链接click here

mysql> LOCK TABLE t WRITE, t AS t1 READ;
mysql> INSERT INTO t SELECT * FROM t;
ERROR 1100: Table 't' was not locked with LOCK TABLES
mysql> INSERT INTO t SELECT * FROM t AS t1;

LOCK TABLE users WRITE, users AS t1 READ;
INSERT INTO users (nick, email)
SELECT :nick, :email
FROM Dual
WHERE NOT EXISTS (
    SELECT *
    FROM users AS t1
    WHERE nick = :nick OR email =  :email
)
UNLOCK TABLES;