MySQL如何同时管理来自多个用户的多个查询?

时间:2010-10-03 22:07:14

标签: php sql mysql

只是举个例子:

我有一个管理用户投票的PHP脚本。

当用户投票时,脚本会进行查询以检查某人是否已投票选择相同的ID /产品。如果没有人投票,则它进行另一个查询并将ID插入一般ID投票表,另一个将数据插入每用户ID投票表。而这种行为在其他类型的脚本中重复出现。

问题是,如果两个不同的用户同时投票,那么代码的两个实例可能会尝试插入一个新的ID(或某种类似的查询),这会产生错误吗?

如果是,我如何防止这种情况发生?

感谢?

重要说明:我正在使用MyISAM!我的网站托管不允许使用InnoDB。

4 个答案:

答案 0 :(得分:6)

  

问题是,如果两个不同的用户同时投票,则可能是两个实例   代码尝试插入一个新的ID(或一些类似的查询),将提供错误

是的,您最终可能会进行两次插入查询。根据表上的约束,其中一个会生成错误,或者您最终会在数据库中生成两行。

我相信你可以通过应用一些锁定来解决这个问题。 例如如果您需要为ID为theProductId :(伪代码)

的产品添加投票
START TRANSACTION;
//lock on the row for our product id (assumes the product really exists)
select 1 from products where id=theProductId for update;
//assume the vote exist, and increment the no.of votes
update votes set numberOfVotes = numberOfVotes + 1 where productId=theProductId ;
//if the last update didn't affect any rows, the row didn't exist
if(rowsAffected == 0) 
  insert into votes(numberOfVotes,productId) values(1,theProductId )
//insert the new vote in the per user votes
insert into user_votes(productId,userId) values(theProductId,theUserId);
COMMIT;

更多信息here

MySQL提供了另一种解决方案,可能适用于insert on duplicate

e.g。你可以做到:

 insert into votes(numberOfVotes,productId) values(1,theProductId ) on duplicate key 
   update numberOfVotes = numberOfVotes  + 1;

如果您的投票表在产品ID列上有唯一键,则上述内容将为 如果特定的theProductId不存在则执行插入,否则它将执行更新,其中numberOfVotes列增加1

如果您在将产品添加到数据库的同时在投票表中创建了一行,则可以避免大量此操作。这样你就可以确定你的产品总是有一行,只需在那一行发出UPDATE。

答案 1 :(得分:1)

  

问题是,如果两个不同   用户同时投票   可能是两个实例   代码尝试插入一个新的ID(或一些   类似的查询类型)将给出   错误??

是的,一般情况下这是可能的。这是并发系统中一个非常常见的问题的示例,称为race condition

避免它可能相当棘手,但一般来说,您需要确保操作不能以您描述的方式交错,例如通过锁定数据库一段时间。

有几种实用的解决方案,都有自己的优点和风险(例如死锁)。有关讨论和进一步的信息指示,请参阅维基百科文章。

答案 2 :(得分:0)

最简单的方法:

LOCK TABLES table1 WRITE, table2 WRITE, table3 WRITE 
-- check for record, insert if not exists, etc...
UNLOCK TABLES

如果每秒不多次投票,那么上述内容就足够了。

InnoDB表提供了交易,这在这里也很有用。其他人已对此发表评论,因此我不会详细介绍。

或者,您可以在代码级别通过使用某种共享内存互斥来解决它,该互斥禁用并发执行该部分PHP代码。

答案 3 :(得分:-4)

当单身模式派上用场时。它确保代码仅在一瞬间由一个进程执行。

http://en.wikipedia.org/wiki/Singleton_pattern

您必须为数据库访问创建一个单例类,这样可以防止您出现您所描述的错误类型。

干杯。