Sql server 在选择插入过程中是否需要锁定提示?
我有一个存储过程,如果行键不存在,它会将一行插入表中。看起来是这样的:Sql server 在选择插入过程中是否需要锁定提示?,sql-server,Sql Server,我有一个存储过程,如果行键不存在,它会将一行插入表中。看起来是这样的: create proc EmployeeInsertIfNotExists (@id int, @name varchar(50)) as begin SET XACT_ABORT ON begin transaction if not exists(select * from tbl where id = @id) insert into tbl(id, name)
create proc EmployeeInsertIfNotExists
(@id int, @name varchar(50))
as
begin
SET XACT_ABORT ON
begin transaction
if not exists(select * from tbl where id = @id)
insert into tbl(id, name)
values(id, name)
commit transaction
end
这个存储过程实际上只是两个语句,一个select语句和一个可能的insert语句。我在一个事务中处理这两个语句,以便它们之间不会发生任何导致异常的事情。id
列是主键,因此我希望确保不会重复插入相同的id
我的问题是:这是否足以预防问题?我需要在select语句中添加任何提示吗?如果是,我是否需要HOLDLOCK、TABLOCKX
?这对我来说是新材料
编辑:建议答案
create proc EmployeeInsertIfNotExists
(@id int, @name varchar(50))
as
begin
SET XACT_ABORT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
begin transaction
if not exists(select * from tbl where id = @id)
insert into tbl(id, name)
values(id, name)
commit transaction
end
您希望将事务隔离级别标记为
serializable
。否则,有人可以在事务的中途插入具有相同ID的行。这被称为“幻影行”
你不需要把整张桌子都锁上。通过使用正确的隔离级别,SQL Server可以更智能地应用其锁。那么我是否需要将我的过程更改为粘贴的第二个过程?我可以使用merge`语句完全避免事务吗?是的,您的第二个版本应该可以工作。不,合并不是原子的。它与您的原始版本有相同的问题。这对于您正在尝试的操作来说是一种过分的杀伤力。Serializable将损害(杀死?)您机器的性能。如果id是唯一的,则可以保证其他人不能在该列中插入超过一个键值。您可以从进程中获得一个返回值,该值指示插入失败,您可以使用另一个键值重试。
Serializable
是否会实际影响性能取决于许多因素,包括索引方案、写入争用量、SQL Server选择使用的锁类型、,同样,错误处理和重试逻辑可能更便宜或更昂贵,这取决于这两个因素、需要重试的可能性以及是否会发生死锁。但无论如何,两者都比使用表锁好得多。