Php 代码有时会创建重复的行?

Php 代码有时会创建重复的行?,php,mysql,sql,pdo,race-condition,Php,Mysql,Sql,Pdo,Race Condition,我有以下代码: $sth = $dbh->prepare("SELECT * FROM stats WHERE player_id = :player_id AND data_type = :data_type"); $sth->bindParam(':player_id', $player_id); $sth->bindParam(':data_type', $total_time_data_type_id); $sth->execute(); $result =

我有以下代码:

$sth = $dbh->prepare("SELECT * FROM stats WHERE player_id = :player_id AND data_type = :data_type");

$sth->bindParam(':player_id', $player_id);
$sth->bindParam(':data_type', $total_time_data_type_id);
$sth->execute();

$result = $sth->fetch();            

if(!$result){
    $sth = $dbh->prepare("INSERT INTO stats (player_id, offset, created, modified, last_check, data_type, data) VALUES (:player_id, :offset, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '1', :total_time_data_type_id, '0')");

    $sth->bindParam(':player_id', $player_id);
    $sth->bindParam(':offset', $offset);
    $sth->bindParam(':total_time_data_type_id', $total_time_data_type_id);
    $sth->execute();

    if(!$sth){
        return false;
    }
    $sth = $dbh->prepare("SELECT * FROM stats WHERE player_id = :player_id AND data_type = :data_type");

    $sth->bindParam(':player_id', $player_id);
    $sth->bindParam(':data_type', $total_time_data_type_id);
    $sth->execute();

    $result = $sth->fetch();
    if(!$result){
        return false;
    }
}else{
    $sth = $dbh->prepare("UPDATE stats SET .....");
    //Do more stuff
}
现在,偶尔会创建重复的行(在约600行中,有23行重复)。这让我感到困惑,因为在插入行之前,我会显式地检查具有相同
player\u id
data\u type
的行

对于相同的
玩家id
数据类型
,可以存在多个行,但每个行不相同

也就是说,这将是有效的:

ID | PLAYER_ID | DATA_TYPE
---|-----------|----------
1  | 15        | 7
2  | 15        | 18
3  | 92        | 7
4  | 115       | 23
虽然这不会:

ID | PLAYER_ID | DATA_TYPE
---|-----------|----------
1  | 15        | 7        
2  | 32        | 18       
3  | 15        | 7        
4  | 115       | 23
因此,我不能简单地将
player\u id
字段声明为唯一字段


我能想到的唯一可能导致这个问题的原因是,上面的代码片段位于
foreach
循环中,平均约115次迭代,并且该代码在几秒钟内再次被调用。有没有办法通过编程来防止这种情况发生?

感谢大家的帮助,尤其是@nikita2206。这就是我解决问题的方法:

  • 删除了作为主键的列ID
  • 在上创建了新的主键(玩家id、数据类型)
  • 已更改的INSERT语句:


  • 通过多次快速调用代码进行测试,没有创建重复的代码。

    如果同时运行两个代码副本,会发生什么情况?第一个可以进行检查,发现需要添加一个玩家;如果第二个副本在该点之后但在添加播放机之前运行,您将得到重复的条目。@andrewsi这是我假设的,但有没有办法通过编程防止这种情况发生?只需使用“插入…重复密钥更新”@DC_2;-nikita2206的建议将在数据库级别执行。您还可以使用
    flock()
    创建锁文件,并确保一次只能运行一个代码实例。player\u id和data\u type不是复合密钥?为什么?
    $sth = $dbh->prepare("INSERT INTO stats (player_id, offset, created, modified, last_check, data_type, data) VALUES (:player_id, :offset, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '1', :total_time_data_type_id, '0')");
    
    $sth = $dbh->prepare("INSERT INTO stats (player_id, offset, created, modified, last_check, data_type, data) VALUES (:player_id, :offset, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '1', :total_time_data_type_id, '0') ON DUPLICATE KEY UPDATE player_id = player_id");