我有一个进程(进程 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