SQL Server:防止存储过程中的脏读

时间:2010-04-26 23:25:21

标签: sql-server sql-server-2005 tsql

考虑一个SQL Server数据库及其两个存储过程:

* 1。在事务中执行3个重要事项的proc:创建客户,调用sproc执行另一个插入,并有条件地插入带有新标识的第三个记录。

BEGIN  TRAN
    INSERT INTO Customer(CustName) (@CustomerName)
    SELECT @NewID = SCOPE_IDENTITY()

    EXEC  CreateNewCustomerAccount @NewID, @CustomerPhoneNumber

    IF @InvoiceTotal > 100000
         INSERT INTO  PreferredCust(InvoiceTotal, CustID) VALUES (@InvoiceTotal, @NewID)

COMMIT TRAN

* 2。存储过程,用于轮询Customer表中的新条目具有相关的PreferredCust条目。客户端应用程序通过每500毫秒调用此存储过程执行轮询。 Customer上的SELECT不涉及交易。

  --not in the Preferred list
   SELECT C.ID
   FROM Customer    AS C
   LEFT JOIN PreferredCust AS PRE ON PRE.CustID = C.ID
   WHERE PRE.CustID IS NULL  

出现了一个问题,即轮询存储过程在Customer表中找到了一个条目,并将其作为结果的一部分返回。问题是它已经把这个记录拿起来,我假设,作为脏读的一部分。该记录最后在PreferredCust中有一个条目,最终在下游产生了问题。

问题

  • 如何通过第二个存储过程明确防止脏读?
  • 我对脏读方案的假设有多大?

环境是SQL Server 2005,其默认配置是开箱即用的。在这两个存储过程中都没有给出其他锁定命中。

这两个存储过程是通过JDBC连接从Java客户端调用的。不知道他们是否使用相同的连接,但SQL事件探查器显示他们正在使用相同的SPID和ClientProcessID

以下是SQL事件探查器显示的内容:

SELECT @@MAX_PRECISION
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SET IMPLICIT_TRANSACTIONS OFF
SET QUOTED_IDENTIFIER ON
SET TEXTSIZE 2147483647
go
EXEC WriteNewCustomer  'CustomerX', 199000
go

--get any customers in the priority 
SELECT @@MAX_PRECISION
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SET IMPLICIT_TRANSACTIONS OFF
SET QUOTED_IDENTIFIER ON
SET TEXTSIZE 2147483647
go
EXEC GetCustomersWithLowInvoice
go

3 个答案:

答案 0 :(得分:3)

您无法阻止脏读。编写者采用独占锁来防止诚实,读取已提交,读取。但是你可以做 nothing 以防止脏读。脏读者必须停止做脏读,期间。

假设轮询Customer表的代码在您的控制之下,解决方案是从查询中删除脏读提示。这可能会引起争用,因为轮询现在会阻止写入。最好的解决方案是启用row versioning

ALTER DATABASE [<DBNAME>] SET ALLOW_SNAPSHOT_ISOLATION ON; 
ALTER DATABASE [<DBNAME>] SET READ_COMMITTED_SNAPSHOT ON;

然后只需从客户那里作为普通查询进行投票,没有任何提示。您的民意调查不会阻止写入,因为行版本将启动并将查询扫描重定向到行的更新前的非锁定版本。

还有一点需要注意:每500毫秒轮询一次?也许你应该使用查询通知机制来使你的缓存失效,请参阅The Mysterious Notification

答案 1 :(得分:1)

默认isolation levelread committed。在该隔离级别下不会发生脏读。

你可能忽略了另一个原因。

答案 2 :(得分:0)

将以下内容放在程序的顶部(或在BEGIN TRAN之前)。

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 

您也可以选择REPEATABLE READSERIALIZABLE。也就是说,我同意Andomar的观点,鉴于默认级别为READ COMMITTED,可能会出现另一个导致隔离级别发生变化的原因。