MySQL:面对严重并发时的正确性(选择…进行更新)

MySQL:面对严重并发时的正确性(选择…进行更新),mysql,concurrency,Mysql,Concurrency,我正在运行一个网站,在那里用户可以收养虚拟宠物。每个宠物都有每个用户的收养限制。例如,你最多可以收养我们的宠物10次。目前我们正在做这样的事情: CREATE TABLE `num_adopted` ( `petid` int(11) NOT NULL, `userid` int(11) NOT NULL, `total` int(11) unsigned NOT NULL, PRIMARY KEY (`petid`,`userid`), ) ENGINE=InnoDB 当有人

我正在运行一个网站,在那里用户可以收养虚拟宠物。每个宠物都有每个用户的收养限制。例如,你最多可以收养我们的宠物10次。目前我们正在做这样的事情:

CREATE TABLE `num_adopted` (
  `petid` int(11) NOT NULL,
  `userid` int(11) NOT NULL,
  `total` int(11) unsigned NOT NULL,
  PRIMARY KEY (`petid`,`userid`),
) ENGINE=InnoDB
当有人收养宠物时,我们:

START TRANSACTION;

SELECT total
FROM num_adopted
WHERE petid=? AND userid=?
FOR UPDATE
我们根据我们的
限额检查总数。如果他们已经采用了
限制
,我们将
回滚
,并告诉用户。否则,我们:

INSERT INTO num_adopted (petid, userid, total)
VALUES (?, ?, 1)
ON DUPLICATE KEY UPDATE total=total+1
然后,我们在另一个表中添加一行以记录他们的新宠物,最后:

COMMIT
这必须在非常高的并发级别下完美地工作

现在,如果限制是10,并且用户已经采用了限制-1宠物,我可以看到第一个
SELECT
中的
FOR UPDATE
注释保证总数将被锁定,这样多个并发采用不会最终将总数视为
limit-1
(这将允许用户超过他们的
限制
)。第一次领养将看到total=limit-1并成功完成,其余将阻止。最终,其余将看到total=limit并拒绝领养另一只宠物

但是如果
限制
=1,而
总数
=0怎么办?多个采用事务是否可以在num_adopted表中看不到任何行(因此总数=0)同时,允许用户收养多个宠物?不清楚更新的
是否可以锁定不存在的行。如果是这样,这种改变的方案会解决问题吗

START TRANSACTION;

INSERT INTO num_adopted (petid, userid, total)
VALUES (?, ?, 1)
ON DUPLICATE KEY UPDATE total=total+1;

SELECT total
FROM num_adopted
WHERE petid=? AND userid=?

检查是否总计>限制,如果是,
回滚
。否则记录采用的pet和
提交

我测试了这两种方法,并通过在检查当前总计和更新它之间添加一个大的sleep()来模拟高并发性。我描述的第一种方法是这样检查的:

<?php

$limit=1;

mysql_query("START TRANSACTION") or die("!");

echo "Checking the user's adoption count...\n";

$result=mysql_query("
    SELECT total
    FROM num_adopted
    WHERE petid=1 AND userid=1
    FOR UPDATE") or die(mysql_error());

$row = mysql_fetch_row($result);

$count = $row ? $row[0] : 0;

echo "Adoption count before adoption is: $count\n";

if ($count>=$limit) {
    echo "Limit reached, not allowing adoption\n";
    mysql_query("ROLLBACK");
    exit;
}

echo "Wait for 5 seconds...\n";
sleep(5);
echo "Recording the new adoption...\n";

mysql_query("
    INSERT INTO num_adopted (petid, userid, total)
    VALUES (1, 1, 1)
    ON DUPLICATE KEY UPDATE total=total+1") or die(mysql_error());

mysql_query("COMMIT") or die(mysql_error());

echo "Pet has been adopted.\n";

?>

我测试了这两种方法,并通过在检查当前总数和更新总数之间添加一个大sleep()来模拟高并发性。我描述的第一种方法是这样检查的:

<?php

$limit=1;

mysql_query("START TRANSACTION") or die("!");

echo "Checking the user's adoption count...\n";

$result=mysql_query("
    SELECT total
    FROM num_adopted
    WHERE petid=1 AND userid=1
    FOR UPDATE") or die(mysql_error());

$row = mysql_fetch_row($result);

$count = $row ? $row[0] : 0;

echo "Adoption count before adoption is: $count\n";

if ($count>=$limit) {
    echo "Limit reached, not allowing adoption\n";
    mysql_query("ROLLBACK");
    exit;
}

echo "Wait for 5 seconds...\n";
sleep(5);
echo "Recording the new adoption...\n";

mysql_query("
    INSERT INTO num_adopted (petid, userid, total)
    VALUES (1, 1, 1)
    ON DUPLICATE KEY UPDATE total=total+1") or die(mysql_error());

mysql_query("COMMIT") or die(mysql_error());

echo "Pet has been adopted.\n";

?>

为什么不保留它们在会话中的数量,这样您就不必麻烦数据库检查和拒绝它们?因为这对解决并发问题没有任何作用,并且需要很长一段时间的总数。为什么不保留它们在会话中的数量,这样您就不必麻烦数据库检查和拒绝它们?因为这对解决并发性问题没有任何作用,并且在很长一段时间内需要总计。