2010-04-17 71 views
4

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

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

请指教。

感谢

Lijo

+1

可能不是一个好主意 - 看看这篇文章http://www.mssqltips.com/tip.asp?tip=1501 – 2010-04-17 13:12:13

+0

谢谢。它提供了一些亮点。一个问题 - 为什么我们说在悲观条件下使用锁定比时间戳更好?假设user1读取它并去了他的办公室,其他用户在得到错误信息之前必须等很长时间,对吧?如果我们使用时间戳方法,我们不能得到即时回复? 这里缺少的链接是什么? – Lijo 2010-04-17 13:38:44

+0

“悲观条件”意味着锁定 - 这就是为什么很多人知道他们在做什么使用它 – 2010-04-17 13:43:29

回答

4

的首先,你在你的问题描述的方式在我看来与MS SQL ASP.NET应用程序的数据库的最佳方式。数据库中没有锁定。它与永久断开连接的客户端类似于Web客户端是完美的。

如何从一些答案中读到,在术语中存在误解。我们都指使用Microsoft SQL Server 2008或更高版本来保存数据库。如果您在MS SQL Server 2008文档主题“rowversion(的Transact-SQL)”,在打开,你会发现以下几点:

时间戳对于 rowversion数据类型的同义词,并受 数据类型同义词的行为。“...... ”的时间戳语法弃用。 此功能将在 未来版本的Microsoft SQL 服务器中删除。避免在 新的开发工作中使用该功能,并计划 修改当前使用 此功能的应用程序。”

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

正如我读取计数器你的问题我读“时间戳”作为类型rowversion数据的列名。我个人更喜欢名字RowUpdateTimeStamp。在AzManDB中(请参阅以数据库为商店的Microsoft授权管理器),我可以看到这样的名称。有时也用于ChildUpdateTimeStamp跟踪分层RowUpdateTimeStamp结构(相对于触发)。

我实现了这个办法,我的最后一个项目,并很高兴。一般来说,你做以下:

  1. 添加RowUpdateTimeStam p柱到你的数据库的每个表类型rowversion(它会在Microsoft SQL Management Studio中被视为时间戳,这是一样的) 。
  2. 你应该建立你的SQL SELECT查询发送结果到客户端的话,你与主数据一起发送额外RowVersion值。如果你有缝一个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 

或进行数据铸像下面

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 

举行RowUpdateTimeStampBIGINT,其对应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 
  • 所有断开客户机(网络客户端)不仅接收和保存一些数据行,而且还保存数据行的RowVersion(类型ulong)。
  • 在试图修改断开客户端的数据时,客户端应该将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 
    

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

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

    顺便说一下,基于RowUpdateTimeStamp,可以为数据创建非常好的价值ETag它可以在HTTP头与数据一起发送到客户端。通过使用ETag,您可以在客户端实施智能数据缓存。

    我不能在这里写完整的代码,但你可以在互联网上找到很多例子。我只想重申,在我看来,使用乐观并发基于rowversion一次在大多数的情况下ASP.NET的最佳途径。

    +1

    但是,感谢您的详细答案,存储过程代码看起来不正确或不完整。 WHERE Id = @ Id AND RowUpdateTimeStamp 0)?我知道这已经3年了,但我只是偶然发现了这一点。 – Mike 2013-02-06 01:50:11

    +0

    @Mike:谢谢,这是一个旧的“剪切和粘贴错误”,错误修改了代码!我更新了STORED PROCEDURE'dbo.spSoftwareUpdate'的代码。我希望现在的代码是正确的。 – Oleg 2013-02-07 19:24:34

    1

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

    +0

    他没有提到穿线。数据库必须能够处理并发访问 - 无论这些来自单个多线程应用程序还是来自多个应用程序实例,或者两者都不在此处,也不在此处。 – 2010-04-17 13:21:39

    +0

    然后,他需要关注自己的酸性和孤立性。 – duffymo 2010-04-17 14:06:49

    2

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

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

    在您的更新语句中,您只需添加一个where子句来检查rowversion值是否与您第一次检索该行时的值相同。如果不是这样,别人已经改变了行(即:它的肮脏)

    而且,从该页面:

    时间戳语法已被弃用。 此功能将在 未来版本的Microsoft SQL 服务器中删除。避免在 新开发工作中使用此功能,并计划到 修改当前使用 此功能的应用程序。

    0

    排版建议是正确的我会说,但它令人失望地看到时间戳将很快被弃用。我的一些旧应用程序因为不同的原因而使用它,然后检查并发性。