Sql事务的并发处理

Lij*_*ijo 5 sql concurrency

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

这种方法足够了吗?还是在任何情况下这种方法都有缺点?

请指教。

谢谢

里约

Ole*_*leg 5

我认为首先,您在问题中描述的方式是 MS SQL作为数据库的ASP.NET应用程序的最佳方式。数据库中没有锁定。与永久断开连接的客户端(如Web客户端)完美匹配。

如何从一些答案中读取内容,在术语上存在误解。我们都表示使用Microsoft SQL Server 2008或更高版本来保存数据库。如果您在MS SQL Server 2008文档中打开主题“行版本(Transact-SQL)”,则会发现以下内容:

时间戳记rowversion数据类型的同义词, 并受数据类型同义词的影响。” …“ 不建议使用时间戳语法。MicrosoftSQL Server的将来版本将删除此功能。请避免在新的开发工作中使用此功能,并计划修改当前使用此功能的应用程序。”

因此,时间戳数据类型的同义词rowversion为MS SQL数据类型。它拥有64位计数器,该计数器存在于每个数据库内部,可以看作@@ DBTS。修改数据库的一个表中的一行后,计数器将增加。

在阅读您的问题时,我将“ TimeStamp”读为类型rowversion数据的列名。我个人更喜欢名称RowUpdateTimeStamp。在AzManDB中(请参阅带有数据库存储的Microsoft Authorization Manager),我可以看到这样的名称。有时还使用ChildUpdateTimeStamp来跟踪分层的RowUpdateTimeStamp结构(与触发器有关)。

我在上一个项目中实现了这种方法,并且感到非常高兴。通常,您执行以下操作:

  1. RowUpdateTimeStam p列添加到类型为rowversion的数据库的每个表中(在Microsoft SQL Management Studio中将其显示为timestamp,这是相同的)。
  2. 您应该构造所有SQL SELECT查询以将结果发送到客户端,以便将其他RowVersion值与主数据一起发送。如果您有一个带有JOINT的SELECT,则应该从两个表中发送最大RowUpdateTimeStamp值的RowVersion
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
Run Code Online (Sandbox Code Playgroud)

或者进行如下的数据转换

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
Run Code Online (Sandbox Code Playgroud)

保持RowUpdateTimeStamp作为BIGINT,其对应ULONG的C#的数据类型。如果您从多个表中进行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
Run Code Online (Sandbox Code Playgroud)
  1. 所有断开连接的客户端(Web客户端)不仅接收和保存数据行,而且接收并保存数据行的RowVersion(类型ulong)。
  2. 在尝试从断开连接的客户端修改数据的过程中,客户端应将与原始数据相对应的RowVersion发送到服务器。该spSoftwareUpdate存储过程可能看起来像
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
Run Code Online (Sandbox Code Playgroud)

dbo.spSoftwareDelete存储过程的代码看起来一样。如果不打开NOCOUNT,则可以生成在许多情况下自动生成的DBConcurrencyException。Visual Studio使您可以使用诸如TableAdapter或的高级选项中的“使用乐观并发”复选框之类的乐观并发DataAdapter

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

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

我无法在此处编写完整的代码,但是您可以在Internet上找到很多示例。我只想重复一次,在基于我看来使用乐观并发rowversion在大多数的情况下ASP.NET的最好方式