PHP和MySQL并发问题

PHP和MySQL并发问题,php,mysql,email,concurrency,pipe,Php,Mysql,Email,Concurrency,Pipe,我有一个电子邮件管道系统(PHP),当收到电子邮件时,它会将电子邮件插入MySQL数据库。这在很大程度上是正确的 但是,当在地址字段中使用多个收件人时,就会出现问题。换句话说,“To:”标题包含如下内容a@example.com, b@example.com". 这是可以理解的,双方都接受了a@example.com及b@example.com因此,两封(身份)电子邮件被插入到数据库中。我只需要数据库中的一个副本 我最初试图解决这个问题,是在继续使用管道脚本之前检查数据库中是否存在电子邮件。i、

我有一个电子邮件管道系统(PHP),当收到电子邮件时,它会将电子邮件插入MySQL数据库。这在很大程度上是正确的

但是,当在地址字段中使用多个收件人时,就会出现问题。换句话说,“To:”标题包含如下内容a@example.com, b@example.com". 这是可以理解的,双方都接受了a@example.com及b@example.com因此,两封(身份)电子邮件被插入到数据库中。我只需要数据库中的一个副本

我最初试图解决这个问题,是在继续使用管道脚本之前检查数据库中是否存在电子邮件。i、 e:

...
$stmt = $mysqli->prepare("SELECT * FROM raw_emails WHERE email = ?");
$stmt->bind_param("s", $raw_email);
$stmt->execute();
$stmt->store_result();
if( $stmt->num_rows() > 0 ) die("Raw email already exists");
$stmt->close()

$stmt = $mysqli->prepare("INSERT INTO raw_emails VALUES(?)");
$stmt->bind_param("s", $raw_email);
$stmt->execute();
$stmt->close();

...
我认为这不起作用,因为管道程序的两个实例(几乎)同时运行,所以两个实例都以0行完成第一个块,并继续插入原始电子邮件两次

我还尝试使用事务来调用存储函数,希望其中一个实例在开始之前会等待另一个实例提交。同样的问题也会出现:

MySQL

CREATE FUNCTION `check_raw`(`raw_message_var` VARCHAR(25000)) RETURNS int(1)
BEGIN

    IF (SELECT COUNT(*) FROM raw_emails WHERE email = raw_message_var THEN
        RETURN 1;
    ELSE 
        INSERT INTO raw_emails (email) VALUES (raw_message_var);
    END IF;

    RETURN 0;

END
...
$mysqli->autocommit(FALSE);
$stmt = $mysqli->prepare("SELECT check_raw(?);");
$stmt->bind_param("s", $raw_email);
$stmt->execute();
$stmt->bind_result($already_exists);
if( !$stmt->fetch() ) die($stmt->error);
$stmt->close();
$mysqli->commit();
if( $already_exists ) die("Raw email already exists");
...
PHP

CREATE FUNCTION `check_raw`(`raw_message_var` VARCHAR(25000)) RETURNS int(1)
BEGIN

    IF (SELECT COUNT(*) FROM raw_emails WHERE email = raw_message_var THEN
        RETURN 1;
    ELSE 
        INSERT INTO raw_emails (email) VALUES (raw_message_var);
    END IF;

    RETURN 0;

END
...
$mysqli->autocommit(FALSE);
$stmt = $mysqli->prepare("SELECT check_raw(?);");
$stmt->bind_param("s", $raw_email);
$stmt->execute();
$stmt->bind_result($already_exists);
if( !$stmt->fetch() ) die($stmt->error);
$stmt->close();
$mysqli->commit();
if( $already_exists ) die("Raw email already exists");
...

长话短说:如何让两个或多个实例只将电子邮件插入数据库一次?

您是否确实在比较电子邮件正文?如果是这样,请不要。我希望您的电子邮件将具有某种唯一标识符(messageId)。在
raw\u电子邮件
表中将其作为主键(或唯一约束)。那应该能解决你的问题。如果没有唯一的messageId,那么从电子邮件中创建一个散列(一个非常好的散列),并将该散列作为raw_emails表中的唯一标识符。但是,一般问题的答案是使用,因此一次只能有一个线程写入表(其他线程被迫等待)。@TJ这不是唯一性问题,而是并发性问题。两个实例都没有在表中找到电子邮件(它们还不存在),因此继续插入它们。是的,先生,我理解。我想在这里指出的是,有了唯一性约束,就不能插入重复数据(但您应该有一种定义唯一性的方法。我提出了一些建议)。我真的不喜欢在每次插入之前锁定表。@TJ我道歉。我给出的示例是我正在使用的系统的简化版本。我现在已经通过更好的消息ID和锁定表的组合解决了这个问题。谢谢