0

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
4

2 に答える 2

0

あなたのワークロードを分析して真の解決策を見つけるには、今は時間が足りません。そこで、別の種類の回答を追加します。デッドロック トランザクションを安全に再試行できます。この問題は、トランザクション全体を再実行するだけで修正できます。再試行を試みる前に、少し遅延を挿入する必要があるかもしれません。

ただし、アプリケーションで発生するすべての制御フローを含め、トランザクション全体を必ず再実行してください。再試行の場合、既に読み取られたデータが変更されている可能性があります。

再試行がまれな場合、これはパフォーマンスの問題ではありません。おそらく、再試行が発生したときにログに記録する必要があります。

于 2013-07-06T11:33:49.150 に答える