1

我编写了一个实质上执行 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应该工作,但似乎太严格了。有更好的选择吗?

4

1 回答 1

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)

根据这个问题的答案,Serializable 似乎是要走的路。

于 2010-05-20T22:05:48.123 回答