2014-03-04 81 views
0

我在名爲「A」的表中有很多信息。名爲「B」的表是空的。SQL鎖定問題

我從表格中的500行,這是不是在表中「B」:

SELECT TOP 500 * FROM A WHERE 
NOT EXISTS ( 
     SELECT * FROM B WHERE 
     (
      A.id1=B.id1 AND A.id2=B.id2 
     ) 

) 

一切運作良好,但我在這裏有一個問題。

我有一個Web應用程序,它調用SQL查詢。想象一下,200人一起打電話給我的應用程序,名爲「B」的表中將會有重複。播種如何鎖定這些行?

這是正確的嗎?

SELECT TOP 500 * FROM A WITH (ROWLOCK) WHERE 
NOT EXISTS ( 
    SELECT * FROM B WITH (ROWLOCK) WHERE 
    (
     A.id1=B.id1 AND A.id2=B.id2 
    ) 
) 

爲了進行更明確:

想象當你打開我的Web應用程序,網頁電話(例如Servlet或者PHP)的SQL查詢。

如果有很多人一起打開我的應用程序,則存在線程問題。而打開的查詢正在將數據寫入名爲B的表中,另一個查詢並行地執行相同的操作。

線程問題:當一個線程讀取500行時,另一個線程可能會讀取相同的數據,因爲第一個線程此時仍在寫入數據,尚未完成。

線程1讀到,從表中 「A」 的數據: 1,2,3,4,5,6,7 ...... 500

線程1個wriote數據表命名爲 「B」: 1,2,3,4,5,6,7,... 495

線程1未完成,線程2從表「a」讀取數據: 496,497 .. 500,.. .. 995

螺紋2寫入 496,497 .. 500,.... 995

然後再線程1個寫數據

495 ... 500

對於爲例

+1

不知道我明白。 select語句如何導致重複? – Andrew

+1

我在這裏有點困惑。在頂部,你聲明表B是空的。這意味着你的NOT EXISTS子句不會實際過濾掉任何東西。永遠。 – Mathmagician

+0

我更新狀態。我也寫在這裏:這是線程問題:當一個線程讀取500行時,另一個線程可能讀取相同的數據,因爲第一個線程此時仍在寫入數據,尚未完成。 必須鎖定讀取行,以便不公開 – grep

回答

0

一個ROWLOCK只會鎖定該行。
轉移到tablelock會影響性能。

我將狀態列添加到InProcess的A.
然後在交易標記中爲true。

所以你的查詢也會有一個where A.InProcess = false。

0

我需要指出的是,你已經張貼的查詢實際上不會寫東西到表B.

話雖這麼說,如果你只是希望鎖定正在工作中的行表A,我會建議在表中添加一個狀態列,並使用類似於下面的存儲過程。

DECLARE @Results TABLE 
(
    id1 int NOT NULL, 
    id2 int NOT NULL --Assuming these are ints 
) 

UPDATE TOP (500) A with (ROWLOCK, READPAST) 
SET [Status] = 'InProcess' 
OUTPUT inserted.id1, 
     inserted.id2 
    INTO @Results 
WHERE [Status] <> 'InProcess' 

SELECT * FROM @Results temp 
INNER JOIN A 
    ON A.id1 = temp.id1 AND A.id2=temp.id2 

當我有多個工作人員來到單個表中尋找要執行的任務時,我已經使用了這種方法。

這裏的關鍵點是:

  1. SELECT語句不鎖或塊,但UPDATE的語句將。
  2. ROWLOCK提示僅用於防止表鎖。它不會奇蹟般地創建行鎖。我們僅提供提示,以便我們不鎖定整個表格。
  3. READPAST提示告訴每個查詢,如果它遇到一個行鎖,它應該繼續下一行,而不是等待釋放鎖。我們提供這個提示,以便可以同時執行多個查詢。
  4. 我們設置了[Status]字段來表示該行正在處理,應該忽略,直到我們決定重置狀態(如果曾經)。這可以防止鎖定釋放後多個查詢達到狀態。
  5. 儘管我們已經使用UPDATE爲我們提供了我們想要的鎖,但我們仍然需要實際檢索一些數據。將OUTPUT語句放入表變量中可讓我們捕獲鍵,以便我們稍後可以合併回表中。

這裏有很多事情要做,也許你不需要所有這些,但這是我發現在防止競爭條件的同時給予高併發性的方法。