2010-10-03 124 views
6

只給你一個例子:MySQL如何同時管理多個用戶的多個查詢?

我有一個管理用戶投票的PHP腳本。

當用戶投票,腳本,使查詢,以檢查是否已經有人投了相同的ID /產品。如果沒有人投票,那麼它會進行另一個查詢並將該ID插入普通ID投票表中,並將另一個ID插入到每個用戶ID投票表中。這種行爲在其他類型的腳本中重複出現。

的問題是,如果兩個不同的用戶同時票其可能的是,代碼的兩個實例試圖插入一個新的ID(或一些類似的類型的查詢),將給出一個誤差Δθ

如果是,我如何防止這種情況發生?

謝謝?

重要提示:我使用的MyISAM!我的虛擬主機不允許InnoDB。

+0

它可能有助於顯示您的表結構(表/列名) – rojoca 2010-10-03 22:21:38

+0

我只是舉一個例子,但爲了幫助您:product_votes表有一個productID,一個votes_count和一個total_votes_value列。 user_product_votes表有一個userID,一個productID和一個user_vote列.. – Jonathan 2010-10-03 23:05:50

+1

我問的唯一原因是在這個例子中,重新組織你的邏輯和表結構似乎會更好,因此同時插入並不重要,而不是試圖阻止他們。你是問一個普遍的問題還是你需要解決你的例子中的具體問題? – rojoca 2010-10-03 23:26:04

回答

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

例如你也許可以只是做:

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

如果您的投票表對產品ID列的唯一鑰匙,上面會 執行插入如果特定theProductId不存在,否則會做一個更新,其中numberOfVotes列增加1

如果在將產品添加到數據庫的同時在票據表中創建了一行,則可能會避免很多此類情況。這樣你可以確定你的產品總是有一排,只需在該行上發佈UPDATE。

+0

交易存在正好解決這個問題。通過使用這樣的事務,可以保證任何查詢都不會在數據庫中看到中間狀態。 – SingleNegationElimination 2010-10-03 23:23:47

+0

+1用於'重複密鑰'。原子操作>手動鎖定。 – egrunin 2010-10-04 01:23:53

-4

這是當單身模式派上用場。它確保代碼僅在一個進程中立即執行。

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

你必須做一個單獨的類的數據庫訪問,這將阻止你的錯誤,你描述的類型。

乾杯。

+0

糟糕的主意,它不會解決PHP中併發數據庫訪問的問題。 – nos 2010-10-03 22:17:35

+1

單例類(即使在典型的Java環境中)也不能解決問題。它只是意味着它只能有一個實例,但這個實例可以被多個線程並行訪問。 – 2010-10-03 22:23:30

+0

感謝您的解釋:) – enokd 2010-10-04 10:11:09

1

的問題是,如果兩個不同的 用戶投票同時其 可能是 代碼嘗試的兩個實例插入一個新的ID(或某些 相似類型的查詢),這將使 錯誤? ?

是,一般這是可能的。這是併發系統中一個非常常見的問題的例子,稱爲race condition

避免它可以是相當棘手的,但一般而言,您需要確保該操作不能在你所描述的方式,例如交錯通過鎖定數據庫一段時間。

有幾個實際的解決方案,都有自己的優勢和風險(例如死鎖)。請參閱維基百科文章以獲取討論和進一步的信息指示。

0

最簡單的方法:

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

如果投票不每秒發生很多次,那麼上面的應該是足夠的。

InnoDB表提供交易,這在這裏也可能有用。其他人已經對此發表了評論,所以我不會詳細討論。

或者,您可以通過使用某種共享內存互斥鎖來禁用該代碼段的併發執行,從而在代碼級別解決此問題。