我编写了一个基本执行ETL任务的linq-to-sql程序,我注意到许多并行化将提高其性能的地方。但是,当两个线程执行以下任务(伪代码)时,我担心会阻止单一约束违规。
Record CreateRecord(string recordText)
{
using (MyDataContext database = GetDatabase())
{
Record existingRecord = database.MyTable.FirstOrDefault(record.KeyPredicate());
if(existingRecord == null)
{
existingRecord = CreateRecord(recordText);
database.MyTable.InsertOnSubmit(existingRecord);
}
database.SubmitChanges();
return existingRecord;
}
}
通常,此代码执行SELECT
语句以测试记录存在,如果记录不存在,则执行INSERT
语句。它由隐式事务封装。
当两个线程为同一recordText
实例运行此代码时,我想阻止它们同时确定该记录不存在,从而试图创建相同的记录。隔离级别和显式事务将很好地工作,除了我不确定我应该使用哪个隔离级别 - Serializable
应该工作,但似乎太严格。有更好的选择吗?
答案 0 :(得分:1)
我使用类似于下面显示的SQL来避免这种情况。 UPDLOCK
指定在事务完成并且HOLDLOCK
等效于SERIALIZABLE
之前采取并保持更新锁。 SERIALIZABLE
通过在事务完成之前保留共享锁来限制共享锁,而不是在不再需要所需的表或数据页时立即释放共享锁,无论事务是否已完成。执行扫描的语义与在SERIALIZABLE
隔离级别运行的事务的语义相同。 HOLDLOCK
仅适用于为其指定的表或视图,并且仅适用于由其使用的语句定义的事务的持续时间。HOLDLOCK
无法在SELECT
中使用包含FOR BROWSE
选项的语句。
declare @LocationID int
declare @LocationName nvarchar (50)
/* fill in LocationID and LocationName appropriately */
INSERT dbo.Location
(LocationID, LocationName)
SELECT @LocationID, @LocationName
WHERE NOT EXISTS (
SELECT L.*
FROM dbo.Location L WITH (UPDLOCK, HOLDLOCK)
WHERE L.LocationID = @LocationID)
根据this question的答案,Serializable似乎是要走的路。