2013-01-09 53 views
0

我有這個TSQL代碼來檢查'sadsadsad'是否存在,是否存在並將其插入到表中。在多線程環境中將記錄插入到數據庫表中

if not exists(select id from [ua_subset_composite] where ua = 'sadsadsad') 
    begin 
    insert into [ua_subset_composite] 
    select 'sadsadsad',1,null,null,null,null 
    end 

我擔心的是,在生產的地方會有同時運行多個線程,可能會出現一種情況,記錄會滑通之間的不存在選擇和插入。

我不希望添加的列,不知道的唯一約束,如果我能改善這個SQL代碼,以便它會保證唯一性

+5

這將是有趣的,知道你爲什麼不想在顯然應該是唯一的列上添加一個'UNIQUE'約束?如果重複次數很少,那麼只要嘗試插入並捕獲由此產生的錯誤即可,這是一種合理的方法。 – Pondlife

+2

使用唯一約束是解決此問題的唯一明智的解決方案。 –

+0

+1的獨特約束...我可以問爲什麼你想避免這種情況? –

回答

0

這是我最後做

insert into [ua_subset_composite] WITH (TABLOCKX) (ua, os) 
select @r, 1 
where not exists (select 1 from [ua_subset_composite] nolock where ua = @r 

爲了測試我同時運行這段代碼從多個窗口

代碼
declare @r nvarchar(30); 
while(1=1) 
begin 

set @r = convert(nvarchar(30),getdate(),21) 
insert into [ua_subset_test] WITH (TABLOCKX) (ua, os) 
select @r, 1 
where not exists (select 1 from [ua_subset_test] nolock where ua = @r 

) 
end 
0

你可以實現你的數據庫上的鎖定策略。您有悲觀的選擇:

當您鎖定記錄以供專用時,直到您完成 。它具有比樂觀鎖定更好的完整性 ,但要求您注意應用程序設計以避免出現死鎖。

或樂觀:

,你讀取記錄,注意到一個版本號,並檢查 版本你寫的記錄回之前並沒有改變。當您將 寫回記錄時,您可以過濾版本上的更新以確保它是原子性的。 (即在您檢查 版本並將記錄寫入磁盤之間未更新),並在 中一次更新版本。

如果記錄髒(即與您的版本不同),您可以中止 事務,用戶可以重新啓動它。

source

0

當執行select,放置一個updlockholdlock上的範圍被選擇:

begin transaction 

if not exists(
    select id 
    from [ua_subset_composite] with (updlock, holdlock) 
    where ua = 'sadsadsad') 
    begin 
    insert into [ua_subset_composite] 
    select 'sadsadsad',1,null,null,null,null 
    end 

commit 

holdlock,相當於serializable isolation level,將具有以下效果:

  • 語句不能讀取已被修改但尚未被其他事務處理的數據。

  • 沒有其他事務可以修改當前事務讀取的數據,直到當前事務完成。

  • 其他事務不能插入具有鍵值的新行,這些鍵值落在 當前事務中的任何語句讀取的鍵的範圍內,直到當前事務完成。

範圍鎖被放置在匹配在事務執行的每個語句的 搜索條件的關鍵值的範圍。從更新或插入該 有資格獲得任何當前 交易執行的語句中的任意行。這 塊的其他交易。這意味着如果第二次執行事務 中的任何語句,它們將讀取相同的一組行。 範圍鎖定一直持續到事務完成。這是對隔離級別限制最嚴格的原因,因爲它鎖定了整個範圍的 鍵,並保留鎖直到事務完成。 因爲 併發性較低,所以只有在必要時才使用此選項

updlock需要除了holdlock ...加入我們避免在同一時間在同一範圍內執行自己的select with (updlock, holdlock)聲明一個單獨的進程中updlock。爲解決這一

+0

這對嘗試插入的人有什麼影響?轟炸他們的交易? – Woot4Moo

+0

@ Woot4Moo嘗試插入的其他進程將被阻塞,並等待阻塞進程完成並釋放鎖,然後再繼續。不一定會彈出,但肯定會降低併發性。 –

+0

好的,澄清雖然,這不會導致髒插入/更新?我可能是錯的,只是想澄清一下。 – Woot4Moo

2

的一種方法是使用隔離的較高水平(即鎖定)。您可以將整個語句包裝在事務中,並使用更嚴格的隔離級別。

例如:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 

BEGIN TRANSACTION 

    <your code here> 

COMMIT TRANSACTION 
+0

同意答案給定的問題給定一個獨特的約束不是一個選項。 – Paparazzi

+1

如果兩個進程同時運行,'SERIALIZABLE'本身會導致死鎖...'updlock'也是需要的...... –

-1

不幸的是,上述答案是正確的。當心任何啓動了「鎖定」的解決方案,即BEGIN TRAN SELECT。是的,如果隔離級別爲SERIALIZABLE,則SELECT會創建鎖,以防止其他進程更新所選數據。但是如果沒有選擇數據呢?什麼是鎖定?

督察,BEGIN TRAN建立一個競爭條件:

/* spid */ 
/* 1 */ SELECT ... -- returns no rows 
/* 2 */ SELECT ... -- returns no rows 
/* 1 */ INSERT ... -- whew! 
/* 2 */ INSERT ... -- error 

要在寫入前閱讀(比如,呈現數據給用戶),有特殊的時間戳數據類型。就你而言,這只是一個插入。使用原子事務,即單個語句:

insert into [ua_subset_composite] (column1, column2) 
values ('sadsadsad', 1) 
where not exists (
    select 1 from ua_subset_composite 
    where column1 = 'sadsadsad' 
) 

服務器保證該行是或未被插入。鎖定是由你知道如何在最短的時間內爲你完成的。 :-)

我不希望添加唯一約束

那麼,你應該,你知道的。以上代碼將阻止嘗試添加非唯一值,並避免出現錯誤消息。一個獨特的約束可以防止某人在成功執行時不那麼謹慎。

+0

「但是如果沒有選擇數據會怎麼辦?什麼是鎖定? 'SERIALIZABLE'將發出範圍鎖定...即使在選擇時數據不存在,*條件*在交易期間將保持鎖定狀態。儘管select語句是insert的一部分,但您的答案與OP原始查詢具有完全相同的競爭條件,但這並不妨礙兩個進程同時運行select。 –

+0

請。子查詢不會創建兩個語句;它是一個。它是原子的。這是正確的。這是標準的。而且速度很快。而且,我不相信像你說的那樣,可序列化的工作,參見。 http://blogs.msdn.com/b/sqlserverstorageengine/archive/2006/05/26/range-locks.aspx。它使用範圍鎖定,這取決於索引。 –

+0

是的,但是'insert into ... select'語句的'select'部分不是子查詢。它是一個在當前隔離級別下運行的標準'select'語句...如果隔離級別是'read committed',那麼'select'語句將只發布共享鎖,即使它與插入聲明。 [請參閱此問題和答案,瞭解有關此主題的其他詳細信息和測試用例。](http://stackoverflow.com/questions/1994771/in-tsql-is-an-insert-with-a-select-statement-safe併發性) –

相關問題