不清楚我需要哪种类型的锁以避免在唯一字段

时间:2017-05-05 20:25:44

标签: sql-server tsql sql-server-2012

我需要检查表中是否存在唯一(非主键)字段,如果不存在 - 请插入:

ID unqiueidentifier, --PK
name varchar(100),
otherID int --Unique

也许使用条件插入:

DECLARE @OtherID int
INSERT INTO TableA (OtherID)
SELECT @OtherID
WHERE NOT EXIST (SELECT * from TableA where OtherID = @OtherID)

SELECT MainID from TableA where OtherID = @OtherID

参考:Whilst locked - see if otherID exists, if so return mainID, if not insert otherID & return new mainID

我需要哪种锁/提示来确保并发进程在检查和插入之间的时间内不插入相同的OtherID(显然并发性仍然是一个问题,即使在单个语句中,例如上文)。

参考:http://kejser.org/race-condition-when-creating-unique-values/

参考:http://weblogs.sqlteam.com/dang/archive/2007/10/28/Conditional-INSERTUPDATE-Race-Condition.aspx

我在HOLDLOCK / UPDLOCK找到了很多信息。我理解他们如何能够暂停选择或更新现有行。什么是没有意义的是他们如何锁定 INSERTING 新行而基本上只是锁定整个表 - 又名TABLOCK

如何锁定不存在的行?!或者UPDLOCK只是锁定整个表格,因为你是SELECT *

如果它确实锁定整个表 - 这似乎可能非常具有破坏性并且不适合缩放?!也许我只是强制执行UNIQUE约束并允许偶尔出现异常?

我在这里看不到任何好的选择?!

2 个答案:

答案 0 :(得分:3)

这是upserts的一个很好的参考,但它也适用于这种情况:Sam Saffron upsert approach

要获取key range lock(以便不锁定整个表格),您需要使用OtherId作为关键字的索引,优先使用with (updlock, serializable)(相当于with (updlock, holdlock))。

declare @OtherId int;

insert into TableA (OtherId)
select @OtherId
where not exists (
  select OtherId 
  from TableA with (updlock, serializable) 
  where OtherId = @OtherId
)

select Mainid 
from TableA 
where OtherId = @OtherId;

参考:

答案 1 :(得分:2)

这里有两个选择。

悲观方法(期望最差)将使用可序列化隔离级别:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE,这将阻止并发事务在您查找时插入冲突数据(幻像行)并可能插入您的独特价值。这会降低并发性,因为其他事务在完成查找和插入时都必须等待。

乐观方法(希望最好)是简单地插入您的ID并捕获并处理异常(如果发生)。这提供了更高的并发性,但使代码更复杂。

选择哪种方法取决于您的性能要求和冲突的可能性;如果您认为冲突很少或需要最高的并发性,那么乐观的方法将更为可取。否则,您将不得不承受悲观方法的并发后果,以确保数据完整性。