我有一个进程(进程A),它不断地向SQL表(表A)添加记录(使用存储过程直接插入)。这是一个连续的进程,它读取请求并写入表。请求是如何到来的没有模式。每天的最大请求数约为100 K。
请求进入后,我需要对这些请求进行一些处理。(由于许可问题)。我目前的做法是使用可执行文件(进程B)在每个用户上运行,这个进程读取和执行一些工作,并写入同一个表。因此,表由多个进程读取/写入。进程B具有以下逻辑
- 获取尚未由其他用户处理且当前未由其他用户处理的记录
- 通过将标志标记为正在处理(C# LINQ到SP)来锁定此运行的记录。这是单个SQL事务,即锁定记录并获取它们以进行处理 Package 在一个事务中
- 处理记录。这是计算发生的地方。这里没有数据库工作。
- 在表A中插入/更新记录(C# LINQ通过db.submitchanges)。这是死锁发生的地方。这是一个单独的SQL事务。
有时,我在写入表时会看到死锁。此SQL Server 2008(隔离级别为Read committed)。对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]
这是获取要处理的数据的proc。
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
2条答案
按热度按时间cyvaqqii1#
我猜您在尝试并发更新时会在页面上出现死锁。
由于更新和插入的性质(基于getdate的滑动时间范围窗口),似乎很难实现一个好的分区方案。
bmp9r5qi2#
我现在没有时间来分析你的工作量并找到真正的解决办法。所以我将添加一个不同的答案:**您可以安全地重试死锁事务。**只需重新运行整个事务即可修复此问题。在尝试重试之前,可能需要插入一点延迟。
请务必重新运行整个事务,包括应用程序中发生的任何控制流。在重试的情况下,已经读取的数据可能已经更改。
如果很少重试,这不是性能问题。您可能应该记录重试发生的时间。