l--*_*''' 20 sql t-sql sql-server
我有100个线程,每个线程调用下面定义的存储过程.
如何防止脏读?
SET QUOTED_IDENTIFIER OFF
SET ANSI_NULLS OFF
GO
ALTER procedure GetNextCerealIdentity
(@NextKey int output, @TableID int)
AS
declare @RowCount int, @Err int
set nocount on
select
@NextKey = 0
begin transaction
Again:
/*Update CfgCerealNumber Table */
UPDATE CfgCerealNumber
SET CerealNumber = CerealNumber + 1
WHERE CerealNumberID = @TableID
SELECT
@RowCount = @@RowCount,
@Err = @@Error /*Obtain updated Cereal number previously incremented*/
IF @Err <> 0 /* If Error gets here then exit */
BEGIN
RAISERROR ('GetNextCerealIDSeries Failed with Error: %d TableID: %d ', 16, 1, @Err, @TableID)
ROLLBACK TRANSACTION
set nocount off
return 1
END
IF @RowCount = 0 /* No Record then assume table is not */
/* been initialized for TableID Supplied*/
BEGIN
RAISERROR('No Table Record Exists in CfgCerealNumber for ID:%d ', 16, 1, @TableID)
set nocount off
Rollback Transaction
return 1
END
/*Obtain updated Cereal number previously incremented*/
SELECT @NextKey = CerealNumber
FROM CfgCerealNumber
WHERE CerealNumberID = @TableID
SELECT @Err = @@Error /*Obtain updated Cereal number previously incremented*/
IF @Err <> 0 /* If Error gets here then exit */
BEGIN
RAISERROR('GetNextCerealIDSeries Failed with Error: %d TableID: %d ', 16, 1, @Err, @TableID)
Rollback Transaction
set nocount off
return 1
END
commit transaction
set nocount off
return 0
GO
Run Code Online (Sandbox Code Playgroud)
看起来这个存储过程的部分在并行运行时返回0.01%的相同值:
SELECT @NextKey = CerealNumber
FROM CfgCerealNumber
WHERE CerealNumberID = @TableID
Run Code Online (Sandbox Code Playgroud)
我通过在事务中包装更新来以这种方式构造我的代码以防止脏读.
如何防止脏读?
如果您需要更新并返回更新的内容,那么我只想使用OUTPUT子句:
UPDATE CfgCerealNumber
SET CerealNumber = CerealNumber + 1
OUTPUT INSERTED.CerealNumber
WHERE CerealNumberID = @TableID;
Run Code Online (Sandbox Code Playgroud)
如果需要进行其他检查,则可以在从存储过程返回结果集之前将OUTPUT输出到声明的表变量中.
另一种方法是首先在表上创建一个阻塞锁,然后更新:
SELECT @CerealNumber = CerealNumber + 1
FROM CfgCerealNumber WITH (HOLDLOCK, UPDLOCK)
WHERE CerealNumberID = @TableID;
UPDATE CfgCerealNumber
SET CerealNumber = @CerealNumber
WHERE CerealNumberID = @TableID;
Run Code Online (Sandbox Code Playgroud)
但我会放钱,我看到这仍然会导致问题.我更相信它.
您可以使用联机丛书@variable = column = expression中描述的语法来避免该问题。此外,由于该语句在单语句自动事务中执行,因此您可以避免显式事务。
SET QUOTED_IDENTIFIER ON;
SET ANSI_NULLS ON;
GO
CREATE PROCEDURE GetNextSerialIdentity
@NextKey int output
, @TableID int
AS
SET NOCOUNT ON;
UPDATE dbo.CfgSerialNumber
SET @NextKey = SerialNumber = SerialNumber + 1
WHERE SerialNumberID = @TableID;
IF @@ROWCOUNT = 0
BEGIN
RAISERROR ('No Table Record Exists in CfgCerealNumber for ID:%d ',
16,1, @TableID);
END
GO
Run Code Online (Sandbox Code Playgroud)