0

我有一个进程(进程 A),它不断将记录添加到 SQL 表(表 A)(使用存储过程直接插入)。这是一个读取请求并写入表的连续过程。请求如何来没有模式。每天的最大请求数约为 100K。

一旦请求进来,我需要对这些请求进行一些处理。这些目前在用户桌面中完成(由于许可问题)。我目前正在做的方式是在每个用户上运行一个可执行文件(进程 B),并且当请求进入表时,该进程读取并执行一些工作并写入同一个表。所以 Table 由多个进程读取/写入。进程B有如下逻辑

  • 获取未被其他用户处理且当前未被其他用户处理的记录

    • 通过标记标志 isProcessing(C# LINQ 到 SP)来锁定此运行的记录。这是一个单独的 SQL 事务,即锁定记录并获取它们以进行处理被包装在一个事务中
  • 处理记录。这是计算发生的地方。这里没有数据库工作。

  • 在表 A 中插入/更新记录(C# LINQ 通过 db.submitchanges)。这就是发生死锁的地方。这是一个单独的 SQL 事务。

有时,我在写入表时会看到死锁。此 SQL Server 2008(具有已提交的隔离级别)。对 SQL 的访问由存储过程和直接 C# Linq 查询完成。问题是如何避免死锁。有更好的整体架构吗?也许,不是所有这些子进程都独立写入表,而是应该将它们发送到一个服务,该服务将它们排队并写入表?我知道如果没有所有代码(太多无法显示)很难回答,但希望我已经解释过了,我很乐意回答任何具体问题。

这是一个有代表性的表结构。

    CREATE TABLE [dbo].[tbl_data](
[tbl_id] [nvarchar](50) NOT NULL,
[xml_data] [xml] NULL, -- where output will be stored
[error_message] [nvarchar](250) NULL,
[last_processed_date] [datetime] NULL,
[last_processed_by] [nvarchar](50) NULL,
[processing_id] [uniqueidentifier] NULL,
[processing_start_date] [datetime] NULL,
[create_date] [datetime] NOT NULL,
[processing_user] [nvarchar](50) NULL,
    CONSTRAINT [PK_tbl_data] PRIMARY KEY CLUSTERED 
    (
[tbl_id] ASC,
[create_date] ASC
    ) ON [PRIMARY]

这是获取数据进行处理的过程。

    begin tran
            -- clear processing records that have been running for more than 6 minutes... they need to be reprocessed...
    update tbl_data set processing_id = null, processing_start_date = null
    where DATEDIFF(MINUTE, processing_start_date, GETDATE()) >=6

    DECLARE @myid uniqueidentifier = NEWID();

    declare @user_count int

    -- The literal number below is the max any user can process. The last_processed_by and last_processed_date are updated when a record has been processed
    select @user_count = 5000 - count(*) from  tbl_data where last_processed_by = @user_name and  DATEDIFF(dd, last_processed_date, GETDATE()) = 0

    IF (@user_count > 1000) 
        SET @user_count = 1000 -- no more than 1000 requests in each batch.

    if (@user_count < 0) 
        set @user_count = 0



    --mark the records as being processed
    update tbl_data set processing_id = @myid, processing_start_date = GETDATE(), processing_user = @user_name from tbl_data t1 join
    (
        select top (@user_count) tbl_id from tbl_data
        where 
            [enabled] = 1 and processing_id is null 
        and isnull(DATEDIFF(dd, last_processed_date, GETDATE()), 1) > 0 
        and isnull(DATEDIFF(dd, create_date, GETDATE()), 1) = 0 
    ) t2 on t1.tbl_id = t2.tbl_id

    -- get the records that have been marked
    select tbl_id from tbl_data where processing_id = @myid 

    commit tran
4

2 回答 2

0

我现在没有时间分析您的工作量并找到真正的解决方案。所以我要添加另一种答案:您可以安全地重试死锁事务。只需重新运行整个事务即可解决此问题。在尝试重试之前,可能需要插入一点延迟。

不过,请务必重新运行整个事务,包括应用程序中发生的任何控制流。在重试的情况下,已读取的数据可能已更改。

如果重试很少,这不是性能问题。您可能应该在重试发生时记录。

于 2013-07-06T11:33:49.150 回答
0

我的猜测是你在尝试并发更新时在页面上死锁。

由于更新和插入的性质(基于 getdate 的滑动时间帧窗口),看起来一个好的分区方案很难实现。没有它,我认为您最好的选择是使用 sp_getapplock http://msdn.microsoft.com/en-us/library/ms189823(v=sql.100) 实现应用程序级锁(相当于互斥锁的 sql) .aspx

于 2013-07-05T15:15:28.937 回答