首页 > 解决方案 > PHP / Mysql issue with concurrent DB calls

问题描述

Using innoDB tables and mysqli wrapper in PHP for queries.

We are currently having an issue where we're getting a spike in traffic requesting the same script 1,500 times per second.

The situtation is that the first X amount of users to access the script win a prize.

The prize is a single record on a table "prizes" that has a count of the # claimed and the # alotted.

Once the used amount >= the alotted amount, we stop awarding prizes.

What is happening is a number of requests to the script are READING the row at the same time before other instances of the script can UPDATE the row, thus showing them each that there is still a # of prizes left to be claimed. This causes us to award more than the alotted amount.

Any ideas on how to circumvent this?

标签: phpmysqldatabase

解决方案


对,你在描述一个经典的比赛条件。

一种解决方案是SELECT...FOR UPDATE在更新奖品表中的行之前使用它来建立锁定。InnoDB 将建立锁的请求顺序,使每个请求等待轮到它,直到它可以获取锁。

然而,这不是一个好的解决方案,因为它会导致每个用户的浏览器不停地旋转,等待响应。在服务器上,您将很快获得每秒 1500 个锁定请求排队。即使每个会话只需要 10 毫秒来完成SELECT FOR UPDATE后续UPDATE工作(这已经相当雄心勃勃了),那仍然是每秒 15.0 秒的工作。到第 2 秒,您将有 30.0 秒的工作要做。

与此同时,用户正在查看他们的浏览器一直挂起,直到轮到他们。这几乎是一个设计的破坏者。

基本上,您需要一些解决方案来建立请求的顺序,即:

  • 全球的
  • 原子
  • 快速地
  • 异步

您可以让每个并发请求使用 AUTO_INCREMENT 键对表执行 INSERT,这将保证它们的顺序。然后,一旦有 X 行,后续请求就不会再插入任何行。

另一种方法是使用消息队列。每个请求只是将自己的请求推送到队列中。然后单个消费者从队列中拉出前 X 个请求,并为他们奖励奖品。队列中的其余请求将被转储,并且不会获得奖励。


推荐阅读