Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
mysql插入(如果不存在)并发会话_Mysql_Concurrency_Insert - Fatal编程技术网

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

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

我想将新用户插入用户表,并确保用户的尼克和电子邮件不在表中(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


有没有更好的方法来处理这个问题?

为什么不使用唯一索引?只要插入新的值,并在违反唯一约束时让查询失败。稍微处理一下错误就可以了

独特的约束也将解决并发用户的问题。

好问题

不幸的是,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;