[英]Parallel Inserts/Updates in a SQL Server table
我有一個多線程環境,每個線程都想要在表中選擇一行(如果不存在,則將其插入)並在其中增加一些內容。
基本上,每個線程都會執行以下操作:
using (var context = new Entity.DBContext()) {
if(!context.MyTable.Any(...)) {
var obj = new MyTable() {
SomeValue = 0
};
context.MyTable.Add(obj)
}
var row = context.MyTable.SingleOrDefault(...);
row.SomeValue += 1;
context.SaveChanges();
}
示例中的問題:特定行的SomeValue =0。兩個線程同時選擇該特定行,它們都看到0。->它們都將其遞增一次,而SomeValue的最終結果將為1,但是我們希望它是2。
我假設緊隨另一個線程到達的線程應該等待(使用鎖?)使第一個線程結束。 但是我不能使其正常工作。
謝謝。
假設使用SQL Server,您可以執行以下操作:
create table T1 (
Key1 int not null,
Key2 int not null,
Cnt int not null
)
go
create procedure P1
@Key1 int,
@Key2 int
as
merge into T1 WITH (HOLDLOCK) t
using (select @Key1 k1,@Key2 k2) s
on
t.Key1 = s.k1 and
t.Key2 = s.k2
when matched then update set Cnt = Cnt + 1
when not matched then insert (Key1,Key2,Cnt) values (s.k1,s.k2,0)
output inserted.Key1,inserted.Key2,inserted.Cnt;
go
exec P1 1,5
go
exec P1 1,5
go
exec P1 1,3
go
exec P1 1,5
go
(請注意,它不一定是一個過程,我只是從一個線程調用它以顯示其工作原理)
結果:
Key1 Key2 Cnt
----------- ----------- -----------
1 5 0
Key1 Key2 Cnt
----------- ----------- -----------
1 5 1
Key1 Key2 Cnt
----------- ----------- -----------
1 3 0
Key1 Key2 Cnt
----------- ----------- -----------
1 5 2
即使有多個線程對此進行調用,我相信它也應該序列化訪問。 我產生的輸出只是為了表明每個調用者也可以知道他們將計數器設置為哪個值(此處為Cnt
列),即使之后另一個調用者立即更改了該值。
如果只有一個進程同時寫入數據庫,則可以將代碼包裝在C# lock(obj) {}
語句中。 這將您限制為一個活動查詢,該查詢不會充分利用數據庫,但是如果可以的話,這是一個簡單的解決方案。
另一種選擇是在定義該行是否已經存在的列上創建唯一索引。 如果insert
,則會得到duplicate key
異常。 您可以在C#中catch
它,然后運行update
。
如果您可以編寫原始SQL,則可以使用鎖定提示,例如with (updlock, holdlock)
或set isolation level serializable
。 這可能會以復雜性為代價為您提供最佳性能。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.