【发布时间】:2017-02-22 03:56:31
【问题描述】:
我有 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
看起来这部分存储过程在并行运行时大约有 0.01% 的时间返回相同的值:
SELECT @NextKey = CerealNumber
FROM CfgCerealNumber
WHERE CerealNumberID = @TableID
我的代码结构是通过将更新包装在事务中来防止脏读。
如何防止脏读?
【问题讨论】:
-
嗯......您使用 ReadCommitted 运行的任何查询都不会给出脏读。那就是定义。并发是这里的问题。这种事情已经被一遍又一遍地讨论过了,而且在 DIY 身份识别过程中存在太多漏洞。为了什么目的?当行被删除时,您仍然会有间隙。一个身份或序列更容易处理,因为所有的漏洞都已经被填满了。
-
脏读是指您的查询返回的数据是未提交事务的一部分。如果你有 ReadCommitted 你不可能得到脏读。当多个线程在完全相同的时刻调用它时,您会遇到竞争条件。当你得到重复时会发生什么,更新语句在表被锁定之前在多个线程中执行。这就是为什么您在 0.01% 的时间内看到相同的值。竞争条件是那些被身份和序列堵塞的令人讨厌的漏洞之一。
-
你说你做到了。 0.01% 来自您的帖子。如果不是这样,那么这里的问题是什么?
-
@SeanLange 为什么下面的 guzman 不同意您的观点:AFAIK,您的原始 proc 可能返回 dups 的唯一原因是它是否在 READ UNCOMMITTED 隔离级别中调用,这允许脏读。无论并发线程数和隔离级别如何,我发布的版本都不会为给定的 SerialNumberID 返回相同的值。
-
SQL 是一个支持 ACID 属性的事务系统(谷歌“SQL ACID”了解详细信息)。为了支持这一点,它将在更新行中的列之前锁定该行。如果出现第二个更新请求,它会锁定并等待第一个请求完成(事务已提交)。这适用于声明事务和隐式事务。
标签: sql sql-server tsql