2012-08-03 56 views
4

在這些情況下sql可能出現競態條件嗎?sql何時獨佔地鎖定更新語句中的一行?

如果我有一個線程調用它的語句1運行這個SQL更新:

更新項目 設置標誌= B 其中標誌= A;

而在另一個調用它的語句2運行這個SQL更新:

更新項目 設置標誌= C 其中標誌= A;

是否有可能讓每個線程讀取Flag等於A的相同記錄並用自己的值寫入記錄?這樣的陳述1可以先寫,然後陳述2寫或反之亦然?

此問題的答案取決於數據庫何時鎖定更新。它發生在找到記錄之前還是在找到記錄並評估where子句之後發生?

+10

你正在使用哪個數據庫? – 2012-08-03 22:16:35

+0

第二條陳述將等到第一條陳述提交。而且它真的很容易使用您正在使用的DBMS進行嘗試。 – 2012-08-03 22:23:05

+0

http://docs.oracle.com/cd/B10501_01/server.920/a96524/c21cnsis.htm – Samson 2012-08-03 22:51:39

回答

0

在此場景中將使用用於數據修改操作(例如INSERT,UPDATE或DELETE)的獨佔鎖定。

獨佔鎖確保多個更新不能同時對同一資源進行。

在這種情況下,您不會得到競爭狀態。

如果您有涉及多個表的更復雜的情況,那麼您可能會遇到競爭條件或死鎖。有許多方法可以避免這種情況,簡化和分隔查詢等。

您還可以將提示應用於告訴SQL要使用的鎖的類型的查詢。

http://msdn.microsoft.com/en-us/library/aa213026(v=sql.80).aspx

+0

因此,在找到與where子句相匹配的記錄之前就會提出排它鎖定?我們確實知道嗎? – Litehouse 2012-08-04 00:06:44

0

聽起來像是你應該閱讀有關鎖定。 SQL服務器具有一組複雜的邏輯,並根據它估計需要更新的行數執行表級或行級鎖。除非你明確告訴它你希望它執行什麼,否則甚至可能因查詢而異。通常如果你修改表的一小部分,它會選擇一個行級鎖。

SQL Server設計時考慮到了ACID,因此它會在對數據執行任何實際更新之前將更改寫入其日誌。這允許任何失敗的更新被回滾並且允許查詢之間的一致性(就像你詢問的那樣)。您可以執行髒讀取以解決鎖定問題,但是無法防止SQL Server鎖定插入,更新和/或刪除的記錄。

SQL Server Locking

編輯:下面是關於ACID的文章。 ACID - Wikipedia

0

所有的SQL數據庫幾乎保證不會發生這樣的衝突。 「何時發生鎖定取決於鎖定位於表,分區,頁面還是行級別。或者,您是否在數據庫中關閉了這種鎖定。

如果您有併發更新語句和多行更新,會發生什麼情況是一行更新了第一行,一行更新了第二行。

一般來說,我認爲where子句被評估爲選擇行集合,一次鎖定一行,執行更新並解鎖。但是,這取決於鎖定的類型。在這種情況下,上述情況會繼續進行值翻轉。

如果您擔心這種情況,請在處理併發更新請求時使用表級鎖定強制序列化。

+0

沒有符合SQL標準的數據庫將「翻轉」這些值。第一個要執行的語句將獲取鎖,更改值並釋放提交時的鎖。一些數據庫會佔用額外的鎖。例如,取決於事務隔離級別,範圍之前和之後的「間隔」可能必須被鎖定。 – 2012-08-04 01:06:21

+0

我沒有說這是標準的行爲。無論如何,這是我的困惑。至少在一個數據庫(SQL Server)上更新時允許讀取未提交的提示...但該提示被忽略。 – 2012-08-04 04:21:18

+0

在某些數據庫中,MVCC用於SELECT語句,但不適用於其他DML。由於這個原因,MySQL InnoDB非SELECT DML是「半可重複」讀取的,因爲最新版本的行總是呈現給非SELECT語句。在任何情況下都不會出現兩個連接在同一行上獲得X鎖的情況。如果發生這種情況,那麼就必須宣佈一個僵局。 – 2012-08-04 04:29:00

4

首先,有三個鎖背景:

  • 數據庫級鎖
  • 表級鎖
  • 行級鎖

那麼您有四個鎖模式:

  • IX
  • IS
  • X
  • 小號

IX和IS鎖是 「意圖」 鎖。這些鎖在獲取其他類型的鎖之前保持不變。 X鎖是獨佔(寫)鎖,S鎖是共享(讀)鎖。

鎖定(IX,IS,X或X)鎖定可以在任何上下文級別進行。例如,數據庫級別的X鎖將阻止數據庫中的所有其他操作。這是SQLlite所採用的鎖的類型。在讀取期間爲整個數據庫採取S鎖,並在寫入期間爲整個數據庫採用X鎖。寫入將等待任何S鎖完成,並將阻止新的S和X鎖,直到寫入鎖被釋放。這提供了一個可序列化的隔離事務級別。

對於MySQL,鎖定取決於存儲引擎。 MyISAM將在整個(一組)表上使用X和S鎖。 X鎖將等待現有的S或X鎖並阻止新的鎖。新的X鎖將在隊列中被賦予更高的優先級,並在新的S鎖之前移動。這種行爲可以通過設置LOW_PRIORITY_UPDATES來改變,這可能導致寫入匱乏,因爲寫入將被優先化而不是讀取。

MySQL中有可能使用'FLUSH TABLES WITH READ LOCK'在整個數據庫上獲得X鎖。

InnoDB通過讀索引來鎖定行。 InnoDB在索引記錄遍歷時鎖定索引記錄並鎖定記錄。 InnoDB使用稱爲'gap'鎖的特殊鎖確保REPEATABLE-READ事務隔離級別。鎖在索引條目上保存,所以如果一個表對於UPDATE查詢沒有很好的索引,那麼許多行將被鎖定。請注意,InnoDB不會爲普通的SELECT查詢創建S鎖。它使用行版本控制,而不是行級鎖定來實現一致的快照。

當獲取X鎖時,數據庫需要檢測死鎖。考慮以下內容:

>connection 1 
start transaction; 
update T set c = c + 1 order by id asc; 

>connection 2 
start transaction; 
update T set c = c - 1 order by id desc; 

在行鎖定模型中,這兩個語句不能同時成功完成。第一個人會永遠等待第二個人獲得鎖,反之亦然。數據庫將選擇其中一個連接回滾。 InnoDB將選擇進行最少數量更改的連接。MyISAM將鎖定整個表,以便首先獲取鎖的連接,然後在第一個連接完成後運行第二個連接。

您給出的簡單示例將由任何上下文(數據庫,表或行)上的X鎖來解決。如果兩個連接以完全相同的類型開始,那麼兩個更新會嘗試更新同一行,兩者都會嘗試獲取X鎖。只有一個連接可以獲取X鎖。無法確定究竟哪一個會獲得鎖定。其他連接必須等到鎖被釋放,直到它可以獲取X鎖。請記住,如果行被DELETE或UPDATE鎖定,那麼服務器可能最終在等待後不會獲取鎖,因爲數據庫中沒有任何內容可以鎖定。

在你的例子中,第一個獲取X鎖的UPDATE,第二個UPDATE將等待X鎖並最終執行但不匹配任何行。

+0

是的,但是他們什麼時候嘗試獲取X鎖? where子句在評估之前還是之後?這就是我試圖理解的。 – Litehouse 2012-08-04 00:25:29

+0

這取決於數據庫。如果使用SQLlite,則在評估WHERE子句之前獲取X鎖,因爲該鎖是DATABASE級別的。如果使用MyISAM,則在計算WHERE子句之前採用X鎖,因爲X子句是TABLE級鎖。如果您使用InnoDB(行級鎖),則在評估WHERE子句期間會採用X鎖,因爲如果沒有通過計算WHERE子句來查找行,您無法確定要鎖定哪些行。這就是爲什麼你用MyISAM獲得序列化的更新,但是在我的死鎖例子中與InnoDB發生了死鎖。 – 2012-08-04 00:31:28

+0

那麼在InnoDB上,它會首先評估行條件,然後當它有匹配時在寫入之前獲取鎖定? – Litehouse 2012-08-04 00:47:24