如何防止两个用户同时访问MySQL表?

4
假设我有一个网站,当你访问它时,会显示你今天的幸运词。问题是每个词只能为一个人带来好运,所以你需要快速访问该网站。下面是一张包含幸运词的示例表格:
+---------------------+
| lucky_word          |
+---------------------+
| cat                 |
| moon                |
| piano               |
| yellow              |
| money               |
+---------------------+

我的问题是:我如何防止两个(或更多)用户同时访问该表。 我假设每个用户从现有表中读取第一个lucky_word,并且所选择的单词立即被删除,以便下一个用户不会看到它。 例如,我想避免cat被显示给多于一个访客。
我应该使用适当的MySQL查询解决这个问题还是在PHP代码中添加几行代码还是两者都需要?

1
https://dev.mysql.com/doc/refman/5.0/en/lock-tables.html - Kenan Zahirovic
@BK435 我正在使用InnoDB。Kenan,谢谢你提供的链接,我会看一下的。 - kozooh
4个回答

7

您可以在事务中使用锁定读取,例如使用PDO:

$pdo = new PDO('mysql:charset=utf8;dbname='.DBNAME, USERNAME, PASSWORD);

$pdo->beginTransaction();

$word = $pdo->query('SELECT lucky_word FROM myTable LIMIT 1 FOR UPDATE')
            ->fetchColumn();

$pdo->prepare('DELETE FROM myTable WHERE lucky_word = ?')
    ->execute(array($word));

$pdo->commit();

0

如果在使用特定单词时将日期戳添加到更新表中,这个想法怎么样?

然后您可以使用以下伪SQL语句...

select word from words where lastdate <> [today];
update words set lastdate = today where word = [word];

2
不能解决并发问题。理论上,如果流量足够大(或者时间非常不巧),另一个会话可能会在此会话的 select 和 update 之间发出 select,从而为两个不同的会话获取相同的单词。您需要获取锁来防止这种情况发生。 - Atli

0
我用于类似任务的快速方法:
1)创建一个仅包含一个字段“id”的表“unique_sequence”-> INT AUTOINCREMENT
 CREATE TABLE `erd`.`unique_sequence` (
   `id` INT NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`));

2) 当用户进入网站时:

INSERT INTO unique_sequence VALUES();
SELECT word FROM lucky_word WHERE id = LAST_INSERT_ID();

由于LAST_INSERT_ID()生成的ID是基于每个连接在服务器上维护的,因此它应该是多用户安全的... 因此我们可以确信每个新用户都会获得与lucky_word表中匹配的唯一ID。

0
在MySQL中,您可以锁定表,以防止其他会话读取和/或写入该表。在WRITE锁的情况下,第一个请求锁的会话将保持该表直到释放,然后第二个会话将获得它直到解锁,依此类推。这样,您可以确保没有两个会话同时访问或操作相同的数据。
在手册中详细了解:

https://dev.mysql.com/doc/refman/5.6/en/lock-tables.html


这种方法的问题在于,整个表格被锁定,直到锁定被释放之前,其他连接无法进行任何活动。这很快会导致应用程序变得无法使用。像InnoDB这样的事务存储引擎提供的行级锁定是一种更好的方法。 - eggyal
@eggyal 如果可用的话,行锁定是更好的解决方案。-也许可以假设此时正在使用InnoDB引擎?我不确定。但是,如果不知道使用的引擎,表锁定将是普遍的解决方案。 - Atli
这是一个链接,无法翻译,请提供更多上下文。 - eggyal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接