26

ストアドプロシージャがあり、同時に実行できないようにしたいと思います。

私の(マルチスレッド)アプリケーションは、このストアドプロシージャを介して、基になるテーブルに対して必要なすべての作業を実行します。

IMO、テーブル自体をロックすることは、実行する必要のある不必要に抜本的なアクションです。したがって、sp_GetAppLock本質的にクリティカルセクションを強制する、について知ったとき、これは理想的に聞こえました。

私の計画は、ストアドプロシージャをトランザクションに含め、spGetAppLockトランザクションスコープを設定することでした。コードは正常に記述され、テストされました。

コードはレビューのために提出され、この関数を呼び出すべきではないと言われました。しかし、「なぜだめなのか」という明白な質問をするとき、私が得ている唯一の理由は非常に主観的であり、あらゆる形式のロックが複雑であることに関係しています。

私は必ずしもこれを購入するわけではありませんが、この構成を避けるべきであるという客観的な理由があるのではないかと思っていました。私が言うように、私の状況を考えると、クリティカルセクションは私にとって理想的なアプローチに聞こえます。

詳細情報:アプリケーションは、2つのスレッドT1とT2でこの上に配置されます。各スレッドは、異なるメッセージM1とM2を待機しています。関連するビジネスロジックでは、処理はM1とM2の両方が到着した後にのみ発生する可能性があるとされています。ストアドプロシージャは、Mxが到着したことをログに記録し(挿入)、Myが存在するかどうかを確認します(選択)。組み込みのロックは、挿入が連続して行われることを確認するために問題ありません。しかし、選択も連続して行う必要があり、ここに組み込まれている機能に加えて何かを行う必要があると思います。

わかりやすくするために、「処理」を1回だけ実行する必要があります。したがって、ストアドプロシージャが誤検知または誤検知のいずれかを返す余裕はありません。ストアドプロシージャが非常にすばやく連続して2回実行されると、両方の「選択」が処理を実行するのが適切であることを示すデータを返す可能性があるのではないかと心配しています。

4

4 に答える 4

9

SQL Serverに組み込まれている同時実行制御メカニズムに依存できない手順は何ですか?多くの場合、クエリは実際の同時実行を可能にするように書き直すことができます。

ただし、このプロシージャを実際に「単独で」実行する必要がある場合は、最初のアクセス時にテーブル自体をロックする方が、への呼び出しを使用するよりもはるかに高速になる可能性がありますsp_GetAppLock。この手順は頻繁に呼び出されるようです。その場合は、最小限の影響で目標を達成する方法を探す必要があります。


テーブルにM1とM2以外の行が含まれていない場合でも、テーブルロックが最善の策です。

複数のスレッドが複数のメッセージを送信している場合は、トランザクションレベルとして「シリアライズ可能」を使用し、挿入を行う前に同じトランザクション内に他のメッセージがあるかどうかを確認することで、よりきめ細かくすることができます。この場合のデッドロックを防ぐには、たとえば次のように両方のメッセージを確認してください。

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;  
BEGIN TRAN;
 SELECT 
 @hasM1 = MAX(CASE WHEN msg_type='M1' THEN 1 ELSE 0 END), 
 @hasM2 = MAX(CASE WHEN msg_type='M2' THEN 1 ELSE 0 END)
 FROM messages WITH(UPDLOCK)
 WHERE msg_type IN ('M1','M2')

 INSERT ...

 IF(??) EXEC do_other_stuff_and_delete_messages;
COMMIT

COMMITの前のIFステートメントでは、挿入前に収集された情報を、挿入した情報と一緒に使用して、追加の処理が必要かどうかを判断できます。

その処理ステップでは、これらのメッセージを処理済みとしてマークするか、同じトランザクション内にあるすべてのメッセージを削除してください。これにより、これらのメッセージを2回処理しないようになります。

SERIALIZABLEは、まだ存在しない行をロックできる唯一のトランザクション分離レベルであるため、最初のselectステートメントはWITH(UPDLOCK)、最初の実行がまだ実行されている間に他の行が挿入されるのを効果的に防ぎます。

最後に、これらはうまくいかない可能性があることに注意する必要がある多くのことです。代わりに、サービスブローカーを確認することをお勧めします。それで3つのキューを使用できます。1つはタイプM1用、もう1つはタイプM2用です。これらのキューにメッセージが到着するたびに、プロシージャを自動的に呼び出して、トークンを3番目のキューに挿入できます。次に、3番目のキューは、両方のメッセージが存在して機能するかどうかを確認するプロセスをアクティブ化できます。これにより、プロセス全体が非同期になりますが、そのために、キュー3の応答を、常に一度に1つのチェックのみを実行するように制限するのは簡単です。

msdnのサービスブローカー。自動メッセージ処理の「アクティブ化」も確認してください。

于 2012-10-25T14:20:35.683 に答える
6

sp_GetAppLockは他の多くのツールと同じであるため、誤用、過剰使用、または正しく使用される可能性があります。これは、元のポスターで説明されているタイプの問題と完全に一致します。これは、使用法に関するMSSQLのヒントの投稿です。複数のユーザーが同じSQLServerストアドプロシージャを同時に実行できないようにします http://www.mssqltips.com/sqlservertip/3202/prevent-multiple-users-from-running-the -same-sql-server-stored-procedure-at-the-same-time /

于 2015-06-02T15:54:38.293 に答える
5

sp_getapplockSQLバックエンドを使用するように作り直された一部のレガシーアプリケーションをサポートしているため、常に使用しています。SQLServerのロックモデルは、アプリケーションロジックと完全には一致していません。

「悲観的な」ロックモデルを採用する傾向があります。このモデルでは、ユーザーがエンティティを編集できるようにする前にエンティティをロックし、(NOLOCK)データを読み取るときにヒントを広範囲に使用して、実際のテーブルのネイティブロックからのブロックをバイパスします。sp_getapplockこれにぴったりです。また、大規模なマルチユーザーシステムでクリティカルパスを適用するためにも使用します。あなたはあなたがあなたが置くロックと呼ぶものについて体系的でなければなりません。

このルートを介した多数のユーザー/ロックでパフォーマンスの問題は検出されなかったため、うまく機能しない理由はわかりません。同じ名前のロックを配置するプロセスがある場合、ブロッキングとデッドロックが発生する可能性があることに注意してください。ただし、必ずしも同じ順序である必要はありません。

于 2015-06-08T10:18:48.323 に答える
0

メッセージのセットごとにフラグを設定したテーブルを作成できるため、スレッドの1つが最初に処理を開始した場合、フラグは処理中としてマークされます。

スレッドの1つがレコードに到達したときにレコードが適切にブロックされるようにするには、次を使用します。

   SELECT ... FROM WITH(XLOCK,ROWLOCK,READCOMMITTED) ... WHERE ...

このコードの平和により、レコードに排他ロックが設定されます。つまり、最初にアクセスしたユーザーが行を所有します。次に、変更と更新フラグを実行します。他のスレッドは、最初のスレッドがトランザクションをコミットまたはロールバックするまで排他ロックによってブロックされるため、更新された値を取得します。

これが機能するためには、常にXLOCKを使用してテーブルからレコードを選択する必要があります。これにより、期待どおりに機能します。

お役に立てれば。

排他的ロックの証明:

    USE master
    GO

    IF OBJECT_ID('dbo.tblTest') IS NOT NULL
        DROP TABLE dbo.tblTest

    CREATE TABLE tblTest ( id int PRIMARY KEY )

    ;WITH cteNumbers AS (
        SELECT 1 N
        UNION ALL
        SELECT N + 1 FROM cteNumbers WHERE N<1000
    )
    INSERT INTO
        tblTest
    SELECT
        N 
    FROM
        cteNumbers
    OPTION (MAXRECURSION 0)

    BEGIN TRANSACTION

    SELECT * FROM dbo.tblTest WITH(XLOCK,ROWLOCK,READCOMMITTED) WHERE id = 1

    SELECT * FROM sys.dm_tran_locks WHERE resource_database_id = DB_ID('master')

    ROLLBACK TRANSACTION
于 2012-10-25T15:58:10.380 に答える