Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用并发插入避免SQL中的死锁_Sql_Sql Server_Linq_C# 4.0 - Fatal编程技术网

使用并发插入避免SQL中的死锁

使用并发插入避免SQL中的死锁,sql,sql-server,linq,c#-4.0,Sql,Sql Server,Linq,C# 4.0,我有一个进程a,它不断向SQL表a添加记录,并使用存储过程直接插入。它是一个连续的过程,读取请求并写入表。请求是如何产生的没有模式。每天的最大请求量约为10万 一旦请求进来,我需要对这些请求进行一些处理。由于许可问题,这些操作目前在用户桌面上完成。我目前的做法是在每个用户上运行一个可执行进程B,当请求进入表时,该进程将读取、执行一些工作并写入同一个表。所以表是由多个进程读/写的。过程B具有以下逻辑 获取未被其他用户处理且不可用的记录 当前正在由另一个用户处理 通过标记一个标志isProcessi

我有一个进程a,它不断向SQL表a添加记录,并使用存储过程直接插入。它是一个连续的过程,读取请求并写入表。请求是如何产生的没有模式。每天的最大请求量约为10万

一旦请求进来,我需要对这些请求进行一些处理。由于许可问题,这些操作目前在用户桌面上完成。我目前的做法是在每个用户上运行一个可执行进程B,当请求进入表时,该进程将读取、执行一些工作并写入同一个表。所以表是由多个进程读/写的。过程B具有以下逻辑

获取未被其他用户处理且不可用的记录 当前正在由另一个用户处理

通过标记一个标志isProcessing C LINQ through SP来锁定此运行的记录。这是一个SQL事务,即锁定记录并将其打包到事务中进行处理 处理记录。这就是进行计算的地方。这里没有工作

通过db.submitchanges插入/更新表A C LINQ中的记录。这就是死锁发生的地方。这是一个单独的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

我的猜测是,在尝试并发更新时,您在页面上处于死锁状态

由于更新和插入基于getdate的滑动时间框架窗口的性质,很难实现好的分区方案。如果没有它,我认为最好的选择是使用sp_getapplock实现一个应用程序级锁,它相当于一个互斥锁

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

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


如果重试次数很少,这不是性能问题。您可能应该在重试时进行日志记录。

死锁在哪里:设置isProcessing标志时、插入时还是组合时?您的交易边界在哪里?您的事务隔离是什么?你试过选择。。。更新?db.Submitchanges上发生死锁。隔离级别已读提交。每个步骤都是一个单独的事务。i、 e 1和e 3是单独的交易。在1中,选择记录和更新iProcessing标志位于单个事务中。不,我没有试过。我会调查的。我必须修正原来的问题。只有一个表。我们需要表的表定义。脚本是最简单的方法,包括任何键和索引。从一个死锁中获取死锁图也是非常有帮助的。将聚集索引放在NVARCHAR列上会让我担心-如果您不断地按顺序插入该表,即使用guid或类似的方法,那么您将不断地洗牌该表。您是否看到他在页面上死锁的证据?不,只是根据类似的经验猜测而已。