1

我有一个需要锁定以防止插入的表,但它还需要能够在阻止插入时进行更新。

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),并且两者都不能包含重复的玩家 ID。

不良行为: 记录 A = ( Player1: 123 Player2: 456 ) 记录 B = ( Player1: 456 Player2: 123 )

4

3 回答 3

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;
于 2012-08-16T05:00:05.933 回答
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用于知道插入记录的数量并决定做什么。

于 2012-08-16T04:29:06.680 回答
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。那时,当需要新数据时,最旧和最少使用的数据开始被修剪。

于 2012-08-16T04:37:55.297 回答