SQLテーブル(テーブルA)にレコードを追加し続けるプロセス(プロセスA)があります(ストアドプロシージャを使用した直接挿入)。リクエストを読み取り、テーブルに書き込むのは継続的なプロセスです。リクエストの送信方法にパターンはありません。1 日あたりの最大リクエスト数は約 100K です。
リクエストが届いたら、それらのリクエストに対して何らかの処理を行う必要があります。これらは現在、ユーザーのデスクトップで行われています (ライセンスの問題のため)。私が現在行っている方法は、各ユーザーで実行可能ファイル (プロセス B) を実行することです。要求がテーブルに届くと、このプロセスは読み取りと作業を行い、同じテーブルに書き込みます。したがって、テーブルは複数のプロセスによって読み書きされます。プロセスBには次のロジックがあります
別のユーザーによって処理されておらず、現在別のユーザーによって処理されていないレコードを取得する
- フラグ isProcessing をマークして、この実行のレコードをロックします (SP を介した C# LINQ)。これは単一の SQL トランザクションです。つまり、レコードをロックし、処理のためにそれらを取得することは、トランザクションにラップされます。
レコードを処理します。ここで計算が行われます。ここではデータベースは機能しません。
テーブル A のレコードを挿入/更新します (db.submitchanges を介した 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]
これは、処理するデータを取得する 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