Sql事务的并发处理

时间:2010-04-17 13:08:49

标签: sql concurrency

假设,我即将使用ASP.NET和SQL Server 2005启动一个项目。我必须为此应用程序设计并发要求。我打算在每个表中添加一个TimeStamp列。在更新表时,我将检查TimeStamp列是否与选中时相同。

这种方法是否足够?或者在任何情况下这种方法都有缺点吗?

请建议。

由于

Lijo

4 个答案:

答案 0 :(得分:4)

首先,您在问题中描述的方式在我看来是 最佳方式 ,适用于使用MS SQL作为数据库的ASP.NET应用程序。数据库中没有锁定。它非常适合与永久断开连接的客户一样的Web客户端。

如何从一些答案中读到,术语中存在误解。我们都意味着使用Microsoft SQL Server 2008或更高版本来保存数据库。如果您在MS SQL Server 2008文档中打开“rowversion(Transact-SQL)”主题,您将找到以下内容:

  

时间戳是该词的同义词    rowversion 数据类型并受其约束   数据类型同义词的行为。“...   “不推荐使用时间戳语法。   此功能将在a中删除   Microsoft SQL的未来版本   服务器。避免使用此功能   新的发展工作,并计划   修改当前使用的应用程序   这个功能。“

所以 timestamp 数据类型是MS SQL的 rowversion 数据类型的同义词。它保存64位计数器,该计数器存在于每个数据库的内部,可以看作是 @@ DBTS 。在数据库的一个表中修改一行后,计数器将递增。

在我阅读您的问题时,我将“TimeStamp”视为 rowversion 类型的列名称。我个人更喜欢名称​​ RowUpdateTimeStamp 。在AzManDB中(请参阅Microsoft授权管理器,将Store作为DB)我可以看到这样的名称。有时也使用 ChildUpdateTimeStamp 来跟踪分层 RowUpdateTimeStamp 结构(关于触发器)。

我在上一个项目中实施了这种方法并且非常高兴。通常你会这样做:

  1. RowUpdateTimeStam p列添加到数据库的每个表中,类型为 rowversion (它将在Microsoft SQL Management Studio中显示为 timestamp ,这是一样的。)
  2. 您应构建所有SQL SELECT查询以将结果发送到客户端,以便将其他 RowVersion 值与主数据一起发送。如果你有一个带有JOINT的SELECT,你应该从这两个表中发送 RowUpdateTimeStamp 值的 RowVersion ,如
  3. SELECT s.Id AS Id
        ,s.Name AS SoftwareName
        ,m.Name AS ManufacturerName
        ,CASE WHEN s.RowUpdateTimeStamp > m.RowUpdateTimeStamp
              THEN s.RowUpdateTimeStamp 
              ELSE m.RowUpdateTimeStamp 
         END AS RowUpdateTimeStamp 
    FROM dbo.Software AS s
        INNER JOIN dbo.Manufacturer AS m ON s.Manufacturer_Id=m.Id
    

    或者像下面那样进行数据投射

    SELECT s.Id AS Id
        ,s.Name AS SoftwareName
        ,m.Name AS ManufacturerName
        ,CASE WHEN s.RowUpdateTimeStamp > m.RowUpdateTimeStamp
              THEN CAST(s.RowUpdateTimeStamp AS bigint)
              ELSE CAST(m.RowUpdateTimeStamp AS bigint)
         END AS RowUpdateTimeStamp 
    FROM dbo.Software AS s
        INNER JOIN dbo.Manufacturer AS m ON s.Manufacturer_Id=m.Id
    

    RowUpdateTimeStamp 保存为 bigint ,它对应于C#的 ulong 数据类型。如果从许多表中创建OUTER JOINT或JOINT,则会看到所有表中的构造MAX(RowUpdateTimeStamp)稍微复杂一些。因为MS SQL不支持像MAX(a,b,c,d,e)这样的函数,所以相应的构造可能如下所示:

    (SELECT MAX(rv)
     FROM (SELECT table1.RowUpdateTimeStamp AS rv
          UNION ALL SELECT table2.RowUpdateTimeStamp
          UNION ALL SELECT table3.RowUpdateTimeStamp
          UNION ALL SELECT table4.RowUpdateTimeStamp
          UNION ALL SELECT table5.RowUpdateTimeStamp) AS maxrv) AS RowUpdateTimeStamp
    
    1. 所有断开连接的客户端(Web客户端)不仅接收和保存数据行的某些行,而且还接收 RowVersion (类型 ulong )。
    2. 在尝试从断开连接的客户端修改数据时,您应该将 RowVersion 对应的原始数据发送到服务器。 spSoftwareUpdate存储过程看起来像
    3. CREATE PROCEDURE dbo.spSoftwareUpdate
          @Id int,
          @SoftwareName varchar(100),
          @originalRowUpdateTimeStamp bigint, -- used for optimistic concurrency mechanism
          @NewRowUpdateTimeStamp bigint OUTPUT
      AS
      BEGIN
          -- SET NOCOUNT ON added to prevent extra result sets from
          -- interfering with SELECT statements.
          -- ExecuteNonQuery() returns -1, but it is not an error
          -- one should test @NewRowUpdateTimeStamp for DBNull
          SET NOCOUNT ON;
      
          UPDATE dbo.Software
          SET Name = @SoftwareName
          WHERE Id = @Id AND RowUpdateTimeStamp <= @originalRowUpdateTimeStamp
      
          SET @NewRowUpdateTimeStamp = (SELECT RowUpdateTimeStamp
                                        FROM dbo.Software
                                        WHERE (@@ROWCOUNT > 0) AND (Id = @Id));
      END
      

      dbo.spSoftwareDelete存储过程的代码看起来一样。如果您没有打开NOCOUNT,则可以在场景中自动生成 DBConcurrencyException 。 Visual Studio为您提供了使用乐观并发的可能性,例如TableAdapterDataAdapter的高级选项中的“使用乐观并发”复选框。

      如果您查看dbo.spSoftwareUpdate存储过程,您会发现,我在WHERE中使用RowUpdateTimeStamp <= @originalRowUpdateTimeStamp而不是RowUpdateTimeStamp = @originalRowUpdateTimeStamp。我这样做是因为,具有客户端的@originalRowUpdateTimeStamp的值通常被构造为MAX(RowUpdateTimeStamp),而不是一个表。所以它可以是RowUpdateTimeStamp < @originalRowUpdateTimeStamp。您应该使用严格相等 = 并在此处重现与SELECT语句中使用的相同的复杂JOIN语句,或者像我一样使用&lt; = 构造并保持完全相同的安全性之前。

      顺便说一句,可以基于RowUpdateTimeStamp为 ETag 构建非常好的值,RowUpdateTimeStamp可以将HTTP头与数据一起发送到客户端。使用 ETag ,您可以在客户端实现智能数据缓存。

      我不能在这里编写完整的代码,但你可以在Internet上找到很多例子。我想再重复一次,在我看来,使用基于 rowversion 的乐观并发性 是大多数ASP.NET场景的最佳方式

答案 1 :(得分:2)

在SQL Server中,针对情境类型的推荐方法是创建类型为'rowversion'的列,并使用该方法检查该行中的任何字段是否已更改。

SQL Server保证如果行中的任何值发生更改(或插入新行),它的rowversion列将自动更新为不同的值。让数据库为你处理这个比你自己做的更可靠。

在更新语句中,您只需添加一个where子句来检查rowversion值是否与第一次检索行时的值相同。如果没有,其他人改变了行(即:它很脏)

此外,从该页面开始:

  

不推荐使用时间戳语法。   此功能将在a中删除   Microsoft SQL的未来版本   服务器。避免使用此功能   新的发展工作,并计划   修改当前使用的应用程序   这个功能。

答案 2 :(得分:1)

我不确定应该像这样在数据库中处理并发性。数据库本身应该能够管理隔离和事务行为,但是线程行为应该在代码中完成。

答案 3 :(得分:0)

rowversion建议是正确的我会说,但令人失望的是看到时间戳很快就会被弃用。我的一些OLD应用程序使用它是出于不同的原因,然后检查并发性。

相关问题