2009-11-11 49 views
7

我最近在PHP中一直在做一些web开发工作,这导致我在一般的语言上进行研究。到目前为止,我不需要使用它来与数据库进行交互,但我知道它提供了很多方便的功能。尽管我知道基本的SQL,并且已经在数据库中对数据进行了基本的操作,但我不明白Web/PHP/Javascript/SQL基于PHP/Javascript/SQL的Web开发人员如何能够管理修改相同数据的用户同时。PHP和并发

例如,假设您有一个二十一点的网站,用户在注册时将用户划分为两个团队中的一个。每当用户赢得比赛时,他们的一部分奖金将被添加到该球队的总额中。

因此,可以说对于这是否看起来像这样功能的伪代码:

... 
$total = mysql_query("SELECT score FROM team1"); 
$total = $total + $mytotal; 
mysql_query("UPDATE team1 SET score='".$total."'"); 
... 

如果两个玩家在玩的同时,很可能是他们都将调用之前的其他选择人们有机会增加和更新表格,因此一个用户的更改将立即被覆盖。

我的问题是,如何避免这种情况?它是在代码级使用PHP完成的,还是由您使用的任何数据库提供的功能有助于防止这种情况?

我一直在做一些研究,似乎PHP提供了一个semaphore机制,我也注意到mysql提供了一个LOCK table feature。但是,我不确定这些中的哪一个,如果有的话,在实践中使用。

回答

5

如果你的现实世界的问题比这个简单的例子更大,你应该使用一个事务与锁表一起来确保用户不会相互覆盖。

+0

感谢大家对我是从学习一些伟大的答案 - 但我更期待的是什么更复杂的并发问题包括PHP,最好的做法一行SQL不适合。 (对不起,如果我的例子误导了) – 2009-11-11 04:18:16

11

将逻辑保留在数据库中,这些语义大部分已经为您解决。

update teams 
set score = score + 1 
where team_id = 1 

..或任何得分和任何球队号码。

1

数据库管理并发锁定并确保以原子方式进行更改。这是使用支持ACID事务的数据库的好处。

在大多数情况下,更改足够快,锁定争用最小。但它仍然可能发生,因此您需要检查执行SQL查询后返回的错误状态。如果出现问题,mysql_query()函数会返回FALSE

然后,您可以调用mysql_errno()来找出原因(更新冲突,权限不足,SQL语法错误等)。有关MySQL错误代码参考,请参见http://dev.mysql.com/doc/refman/5.1/en/error-messages-server.html

1

我同意Xepochs的回答,除非你绝对需要,否则不会引入并发问题。关于不同的锁定策略,请参阅here

4

主要数据库旨在处理这种情况。

使用MySQL时,InnoDB存储引擎具有行锁定功能,锁定正在写入的行。因此,针对同一行的任何更新请求都会等待,直到该行上的上一个操作完成。

如果您使用MyISAM存储引擎,那么在更新行时会锁定整个表。这会在多个玩家同时推送更新请求时在服务器上产生阻塞。所以这些都是关于什么数据库,特别是你用于这个目的的存储引擎。

+0

+1什么是mysql行锁定语法? – 2009-11-11 18:23:05

+0

要更新的行会自动锁定在InnoDB中。如果您想在事务中读取行时锁定行,则可以使用SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE。为了区别,我会推荐这部分MySQL参考手册:http://dev.mysql.com/doc/refman/5.1/en/innodb-transaction-model.html – Nirmal 2009-11-12 03:42:14

1

我不敢相信上述答案都没有指出你可能会遇到设计缺陷。

是的,所有的答案都是正确的,mysql可以处理赛车条件和互斥访问表。

但是,如果您遇到这样的问题,现在可能是重新考虑您的设计的时候了。为什么你不保存用户ID,无论谁添加了他们的部分奖金?然后你可以有一个:

INSERT INTO team1(userid, amount) VALUES(32, 100); 
    SELECT SUM(amount) FROM team1 
or 
    REPLACE INTO team1 SET amount = $total 

但如果是我,我不会甚至不屑。我会简单地set_cookie(team_score,team_score +金额),并保持客户端的一切。当我想读取分数时,我会触发一个AJAX请求来读取或写入服务器,并仍然异步更新客户端界面。既然你指出你的web开发是基本的,我建议你看看jQuery框架的异步客户端 - 服务器通信,因为它是迄今为止最简单的方法来实现这一点。

编辑:以及更新每个人的UI时,另一名球员增加量将比分的问题,我建议你看看Backbone.js的这裤袜很好的数据模型,以玩家的客户端,以便他们得到即时当分数改变时更新。

0

数据库已内置并发控制,例如,您可以阅读有关postgres并发控制here。如果我是对的,pgsql的默认设置是乐观锁定和读取提交事务隔离,这意味着每个事务都知道其他未提交事务完成的更改。

在你的情况下,这些设置是可以的,所以你可以简单地使用UPDATE team1 SET score = score + mytotal,并且数据库将很好地处理并发问题。

如果您有更长的选择更新问题,那么您可以通过第一次选择使用SELECT FOR UPDATE查询来手动锁定行。

这是目前最好的做法...