2009-11-11 58 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查詢來手動鎖定行。

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