PHP +锁定MySQL表失败

时间:2012-08-16 04:18:11

标签: php mysql innodb

我有一个表需要锁定才能插入,但是在插入被阻止时也需要能够更新。

function myfunction() {
  $locked = mysql_result(mysql_query("SELECT locked FROM mylock"),0,0);
  if ( $locked ) return false;
  mysql_query("LOCK TABLES mylock WRITE");
  mysql_query("UPDATE mylock SET locked=1");
  mysql_query("UNLOCK TABLES");

  /* I'm checking another table to see if a record doesn't exist already */
  /* If it doesn't exist then I'm inserting that record */

  mysql_query("LOCK TABLES mylock WRITE");
  mysql_query("UPDATE mylock SET locked=0");
  mysql_query("UNLOCK TABLES");  
}

但这还不够,从另一个脚本再次调用该函数,同时从函数的2个调用中发生插入,我不能这样做,因为它导致重复记录。

这是紧急的请帮助。我想在字段上使用UNIQUE,但有2个字段(player1,player2),而NEITHER不能包含播放器ID的副本。

不需要的行为:     记录A =(Player1:123 Player2:456)     记录B =(Player1:456 Player2:123)

3 个答案:

答案 0 :(得分:1)

我刚注意到你在代码中遇到了竞争条件。假设没有错误(请参阅我的评论)......两个进程可以检查并获得“未锁定”结果。 “LOCK TABLES”将序列化他们的访问权限,但他们都会继续认为他们有锁并因此重复记录。

您可以将其重写为:

mysql_query("LOCK TABLES mylock WRITE");
mysql_query("UPDATE mylock SET locked=1 WHERE locked=0");
$have_lock = mysql_affected_rows() > 0;
mysql_query("UNLOCK TABLES");
if (!$have_lock ) return false;

答案 1 :(得分:0)

我建议不要使用锁。相反,在检查数据时,请执行以下操作:

mysql_query("INSERT IGNORE INTO my_table VALUES(<some values here>)");
if(mysql_affected_rows()>0)
{
    // the data was inserted without error
    $last_id = mysql_insert_id();
    // add what you need here
}
else
{
    // the data could not be inserted (because it already exists in the table)
    // query the table to retrieve the data
    mysql_query("SELECT * FROM my_table WHERE <some_condition>");
    // add what you need here
}

IGNORE关键字添加到INSERT语句时,MySQL将尝试插入数据。如果它不起作用,因为具有相同主键的记录已经在表中,它将以静默方式失败。 mysql_affected_rows用于了解插入记录的数量并决定要做什么。

答案 2 :(得分:0)

此处不需要表级锁定,更好地使用行级锁定。 行级锁定意味着只有他们正在修改的一行被锁定。通常的替代方法是在修改期间锁定整个表,或者锁定表的某个子集。行级锁定只是将行的子集减少到仍然确保完整性的最小数量。

在InnoDB事务模型中,目标是将多版本数据库的最佳属性与传统的两阶段锁定相结合。 InnoDB在行级别上进行锁定,默认情况下以Oracle的方式运行查询作为非锁定一致性读取。 InnoDB中的锁表存储空间有效,不需要锁升级:通常,允许多个用户锁定InnoDB表中的每一行,或行的任何随机子集,而不会导致InnoDB内存耗尽。

如果您的问题尚未解决,那么内存大小可能就是问题所在。 InnoDB将其锁表存储在主缓冲池中。这意味着您可以同时拥有的锁数受限于启动MySQL时设置的innodb_buffer_pool_size变量。默认情况下,MySQL将此值保留为8MB,如果您在服务器上使用InnoDB进行任何操作,这将毫无用处。

幸运的是,解决这个问题的方法非常简单:将innodb_buffer_pool_size调整为更合理的值。但是,该修复程序确实需要重新启动MySQL守护程序。根本没有办法随时调整这个变量(截至本文撰写的当前稳定的MySQL版本)。

在调整变量之前,请确保您的服务器可以处理额外的内存使用量。 innodb_buffer_pool_size变量是服务器范围的变量,而不是每线程变量,因此它在MySQL服务器的所有连接(如查询缓存)之间共享。如果你将它设置为1GB,MySQL将不会使用所有这些。当MySQL发现要放入缓冲区的更多内容时,内存使用量将逐渐增加,直到达到1GB。此时,当需要出现新数据时,最旧和最少使用的数据开始被修剪。