mysql插入(如果不存在)并发会话
我想将新用户插入用户表,并确保用户的尼克和电子邮件不在表中(InnoDB) 我的逻辑是:mysql插入(如果不存在)并发会话,mysql,concurrency,insert,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 b
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
有没有更好的方法来处理这个问题?为什么不使用唯一索引?只要插入新的值,并在违反唯一约束时让查询失败。稍微处理一下错误就可以了 独特的约束也将解决并发用户的问题。好问题 不幸的是,mysql不支持“如果不存在则插入”之类的内容 有几种丑陋的解决方案 大多数情况下,最好是在应用程序中处理它。选择“之前”,查看是否有任何结果,只有在没有结果时才插入 然后,您可以在字段上放置一个唯一的键,以确保数据库保持一致 您也可以直接插入并依赖唯一密钥。您将在应用程序中遇到一个必须处理的错误。您可以区分错误,以便显示正确的消息。如果我记得正确,重复密钥将是1062 然而,有一些方法可以通过其他技术来实现这一点 据我所知,其中一个叫做互斥表 它的工作原理是创建第二个表“mutex”,其中有syme键字段作为工作表,我现在称之为“table”。 然后,您可以执行以下操作: isnert into table(field1,field2)在(mutex.field1=table.field1和mutex.field2=table.field2)上从互斥左侧外部联接表中选择(“input1”,“input2”),其中mutex.field1!=“input1”和mutex.field2!=“field2” 我没有测试这个,我希望我能正确地记住这个技巧,最好查一下 也可以将此提升到mre灵活性,例如,您可以在一个表中只允许所需数量的重复项 但这并不能确保数据的一致性,因为数据表也可以访问,所以我真的建议使用第一种方法:尽可能依赖关键约束,并在应用程序中处理错误号,如果不可能,请在应用程序中处理
INSERT INTO users (nick, email)
SELECT :nick, :email
FROM Dual
WHERE NOT EXISTS (
SELECT 1
FROM users
WHERE nick = :nick OR email = :email
)
大多数MySql连接器都有办法使行受到影响,或者您可以基于以下链接
选择ROW\u COUNT()
我依赖约束,所以我知道记录不会在此表中重复。在这种情况下,两个SELECT查询都没有用吗?我应该只检查INSERT查询错误代码吗?是的,我建议这样做。因为SELECT和INSERT语句之间可能会有延迟。因此,您不能真正依赖isnert语句,但您可以依赖error代码。这两个字段都是唯一的,但是如果INSERT查询失败,我如何知道哪个字段是重复的?@spajak,使用前两个选项重新查询数据库。执行一些正确的错误处理,错误消息包含违反的约束的名称。现在您知道哪个字段是重复的,因为您知道哪个字段属于此约束t、 所以regex匹配错误msg..有点棘手。我希望有一个更干净的方法,但我认为我没有更好的选择。嗨@frank heikens,如果表的存储引擎是innodb,并且2个并发连接在启用自动提交的情况下将相同的数据插入表,知道1个或两个插入都会失败吗?在开始执行ch之前使用表锁ecking-我被告知不要使用表锁。因为解锁表的代码可能会失败/永远不会执行。对我来说不起作用-对于高度,并发请求会造成大量死锁。请参阅:
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;