2012-12-27 39 views
2

這是我剛纔的問題的後續行動(你可以跳過它,因爲我在這個職位的問題解釋):
MySQL InnoDB SELECT...LIMIT 1 FOR UPDATE Vs UPDATE ... LIMIT 1MySQL的選擇...用於索引更新有併發問題

環境:

  • JSF 2.1在Glassfish
  • JPA 2.0的EclipseLink和JTA
  • 的MySQL 5.5 InnoDB引擎

我有一個表:

CREATE TABLE v_ext (
    v_id INT NOT NULL AUTO_INCREMENT, 
    product_id INT NOT NULL, 
    code VARCHAR(20), 
    username VARCHAR(30), 
    PRIMARY KEY (v_id) 
) ENGINE=InnoDB DEFAULT CHARSET=UTF8; 

它被填充有20000個記錄像這樣的(product_id是54的所有記錄,code是隨機生成的,獨特的,用戶名設置爲NULL):

v_id  product_id code     username 
----------------------------------------------------- 
1  54   '20 alphanumerical' NULL 
... 
20,000 54   '20 alphanumerical' NULL 

當用戶購買產品54時,他從該表中獲得代碼。如果用戶多次購買,他每次都得到一個代碼(對用戶名沒有唯一的限制)。因爲我準備了高活性我想確保:

  • 沒有併發/死鎖發生
  • 性能不會被鎖定機構將需要

從影響SO問題(見上面的鏈接),我發現,做這樣的查詢速度更快:

START TRANSACTION; 
SELECT v_id FROM v_ext WHERE username IS NULL LIMIT 1 FOR UPDATE; 
// Use result for next query 
UPDATE v_ext SET username=xxx WHERE v_id=...; 
COMMIT; 

但是我發現使用索引只有當一個死鎖問題欄。我想添加一個索引有助於加速一點點,但是在大約19,970條記錄之後(實際上在這個行數相當一致)會造成一個死鎖。是否有一個原因?我不明白。謝謝。

+0

什麼是您的事務隔離級別?你有沒有嘗試減少它到'讀提交'? –

+0

如何獲得TX隔離級別。我正在使用JTA,我不能'em.getTransaction()' – JScoobyCed

回答

2

從一個純理論的角度,它看起來像你是不是在第一個語句鎖定右行(不同的條件比在更新語句;除此之外,您還只能鎖定因爲LIMIT 1一排,而你也可能更新更多之後的行)。

試試這個:

START TRANSACTION; 
SELECT v_id FROM v_ext WHERE username IS NULL AND v_id=yyy FOR UPDATE; 
UPDATE v_ext SET username=xxx WHERE v_id=yyy; 
COMMIT; 

[編輯]

至於你僵局的原因,這是可能的答案(from the manual):

如果沒有索引合適對於你的聲明和MySQL必須掃描整個表 來處理聲明,表 的每一行都被鎖定(...)

沒有索引,SELECT ... FOR UPDATE語句可能會鎖定整個表,而使用索引時,它只鎖定一些行。因爲您沒有在第一條語句中鎖定正確的行,所以在第二條語句中會獲取額外的鎖。

顯然,如果整個表被鎖定(即沒有索引),就不會發生死鎖。 在第二個設置中肯定會發生死鎖。

+0

我沒有'id'。我只有可以使用的主鍵。我需要更新一個且只有一條記錄,條件是「任何一行用戶名爲null」。 – JScoobyCed

+0

@JScoobyCed然後,您應該只更新第一個查詢中'v_id'標識的記錄。 –

+0

這就是我正在做的事情,並且當'username'編入索引時會造成死鎖。 – JScoobyCed

0

首先,表格的定義是錯誤的。表中沒有tid列,所以我懷疑主鍵是v_id。
其次,如果您選擇更新,則鎖定該行。在第一個事務完成之前的任何其他選擇都將等待該行被清除,因爲它會碰到完全相同的記錄。所以你會等待這一行。
但是,我非常懷疑這可能是一個真正嚴重的問題,因爲首先,你有用戶名,其次,你有產品ID在那裏。在你最初打出的完全相同的記錄中,你很可能沒有多少點擊,即使你這樣做,交易也應該運行得非常快。
你必須明白,通過使用事務,你通常會放棄一致性數據的併發性。無法同時支持數據和併發的一致性。

+0

感謝您注意'tid'。雖然這不是我的問題(我的代碼中的表名和字段大不相同,但我試圖簡化這篇文章中的名稱)。另外,20,000個記錄的product_id也相同。只有「代碼」(這在20,000行中是唯一的)是不同的,但我無法查詢,因爲它是我需要獲取的值。但是,如果併發性的話,我需要得到0%的機會,即2+查詢返回相同的「代碼」。我不需要'完全併發'。我需要能夠序列化(通過代碼或數據庫鎖定方式),對性能的影響很小,以便獲得接近併發性。 – JScoobyCed

+0

然後鎖定行應該不會造成問題。除非更新需要很長時間。鎖定記錄基本上是你唯一的方法,所以我會去解決這個問題。 – Xnoise