代码之家  ›  专栏  ›  技术社区  ›  Steve Guidi

如果不存在事务,我应该为以下插入使用哪个隔离级别?

  •  1
  • Steve Guidi  · 技术社区  · 15 年前

    我编写了一个linq-to-sql程序,它基本上执行一个ETL任务,我注意到许多地方并行化会提高它的性能。但是,我关心的是在两个线程执行以下任务(psuedo代码)时防止违反uniquness约束。

    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 应该有效,但似乎太严格了。有更好的选择吗?

    1 回复  |  直到 15 年前
        1
  •  1
  •   Community CDub    7 年前

    我使用类似下面所示的SQL来避免这种情况。 UPDLOCK 指定在事务完成和 HOLDLOCK 等于 SERIALIZABLE . 可串行化的 通过将共享锁保持在事务完成之前,而不是在所需的表或数据页不再需要时(无论事务是否已完成)释放共享锁,从而使共享锁更具限制性。扫描的语义与在 可串行化的 隔离等级。 霍尔德洛克 仅适用于指定该表或视图的表或视图,并且仅适用于由使用该表或视图的语句定义的事务的持续时间。 霍尔德洛克 不能用于 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 ,序列化似乎是一种方法。