データベースを使用してオブジェクトのロック/ロック解除を追跡する


8

オブジェクトのロック/ロック解除アクションを追跡する必要があります。オブジェクト(契約、パートナーなど)に対するアクションの前に、lockイベントが発行されます。アクションが終了すると、unlockイベントが発行されます。

ロックされているがまだロック解除されていないオブジェクトを取得したい。目標は、クエリをすばやく作成し、デッドロックを回避することです。

以下は表です

create table locks (
    id int identity,
    name varchar(255),
    lock int
) 

insert into locks values('a', 1)
insert into locks values('b', 1)

insert into locks values('c', 1)
insert into locks values('d', 1)

insert into locks values('a', 0)
insert into locks values('c', 0)


insert into locks values('a', 1)
insert into locks values('b', 1)

以下のクエリを使用して、まだロック解除されていないオブジェクトをオブジェクト化します。

select distinct m.name from locks m
    where (select COUNT(id) from locks locked
           where locked.lock = 1 and locked.name = m.name)
        >  (select COUNT(id) from locks unlocked
            where unlocked.lock = 0 and unlocked.name = m.name)

それは正しく動作し、結果abそしてd

私の質問は次のとおりです。-私のソリューションはデッドロックを回避するのに十分ですか?INSERTクエリの実行中に大量にある場合、問題が発生する可能性はありますか?-これを解決する他の(より良い)方法はありますか?

更新

コンテキストを質問に入れなかったことをお詫び申し上げます。上記のデータベース設計は、データベースロックを置き換えるものではありません。

システムから呼び出す外部システムがあります。オブジェクト(契約またはパートナーである可能性があります)で実行する各アクションの前に、システムで呼び出しlockunlockメソッドを実行する必要があります。

最近、サーバーがクラッシュし、サーバーを再起動する必要がある場合があります。残念ながら、すでに呼び出されている実行中のプロセスは、オブジェクトを解放するlockために呼び出す機会がなかったため、unlockシステムが外部のオブジェクトに再度接続するときに、他のいくつかの問題が発生しました。

したがって、各lock呼び出しを追跡する機能を提供したいと考えています。サーバーを再起動したら、unlock以前にロックしたオブジェクトを呼び出します。

私の質問はプロトタイプ DDL を使用していることを指摘してくれたRemus Rusanuに感謝します。DBAに質問を投稿したのは今回が初めてで、FAQを読んでいないことをお詫びします。

ありがとう

回答:


11

目標は、クエリをすばやく作成し、デッドロックを回避することです。

これは非現実的な目標です。デッドロックは、ロックを取得するアプリケーションによって決定され、ロックの実装方法を制御できません。期待できる最善の方法は、デッドロックを検出することです。

レコードとしてのロックの実装には問題があります。行は存続し、実装が完全な場合でも、アプリケーションのクラッシュ時にロックをリークします。applocksでロックを行います。それらは明確なトランザクションセマンティクスを持ち、デッドロックはエンジンによって検出されます。

しかし、あなたが達成しようとしているものを見て、あなたがロックを必要とする可能性は低いですすべてでキューを記述しています(処理可能な次のアイテムを選択して、競合を回避します=>キューで、ロックではありません)。読むキューとしてテーブルを使用します

あなたの特定の実装(ロックの履歴を保持するテーブルを使用)に関しては、正直に言うと、それは災害です。テーブルのデザインから始めることは、意図した使用法に対して完全に不十分です。名前でクエリを実行しますが、テーブルはインデックスのないヒープです。明確な理由もなく、ID列があります。これは単なる「擬似コード」テーブルであると返信することもできますが、これはDBA.SEであり、ここに不完全なDDLを投稿しないでください。

しかし、より重要なのは、実装がロックを実装しないことです!2人のユーザーが同じオブジェクトを2回「ロック」するのを防ぐ方法はありません。あなたの「ロック」は完全に魔法のように正しく振る舞う発信者に依存しています。ロックをアトミックにチェックおよび取得する方法がないため、最も優れたアプリケーションでもこのロックを使用できませんでした。2人のユーザーがチェックして、「a」がロック解除されていると結論付け、同時にレコードを挿入できます。非常に少なくとも、あなたはユニークな制約が必要になります。もちろん、「ステータスを決定するためにロックとロック解除をカウントする」のセマンティクスを壊すでしょう。('a', 1)

申し訳ありませんが、これはFグレードの実装です。

更新

したがって、各ロック呼び出しを追跡する機能を提供したいと考えています。サーバーを再起動すると、以前にロックされたオブジェクトのロック解除を呼び出します。

リモートシステムとの2フェーズコミット分散トランザクションに従事しない場合、「アンロック」を書き込んでからサードパーティシステムで実際に「アンロック」を呼び出すまでの間に多くの競合状態が発生するため、できることは「ベストエフォート」です。 。最善の努力として、これが私の推奨事項です:

  • ロックを追跡する単純なテーブルを作成します。

    CREATE TABLE locks (name VARCHAR(255) NOT NULL PRIMARY KEY);

  • 呼び出す前lockに、テーブルにロックを挿入してコミットします。

  • 呼び出しを行った後、テーブルからロックをunlock 削除しコミットします
  • システムの起動時にテーブルを見てください。前回の実行で残ったロックがある行は、「ロック解除」する必要があります。unlock各行を呼び出し、その行を削除します。保留中のすべてのロックが「ロック解除」されて初めて、アプリの通常の機能を再開できます。

テーブルは小さいままなので、これを提案します。常に、現在のアクティブなロックのみが含まれています。それは完全にサイズが大きいため、悪心を伸ばしたり、後で問題を引き起こしたりすることはありません。ロックされているものを確認するのは簡単です。

もちろん、この実装では、誰がいつロックしたかに関する監査履歴は提供されません。必要に応じて、イベント(ロックまたはロック解除)を挿入するだけで、「孤立した」ロックを把握するためにクエリを実行しない別のテーブルとして、それを追加できます。

あなたはまだあなたのシステムがクラッシュしていないことを保証することはできませんので、起動時に失敗する「ロック解除」への呼び出しで準備する必要がを呼び出すunlockが、前に(行の削除、他の言葉であなたのテーブルおよびサードパーティシステムが離れて漂流し、異なっていています真実のバージョン)。繰り返しになりますが、分散トランザクションを使用せずにこれを防ぐことはできません。DTC はアドバイスません。


4

これにより、アプリケーションの同時実行性が無効になります。SQL Serverには、同じ行を同時に更新しないようにするために必要なすべての機能がすでに備わっているため、実際にそれを行う必要はありません。

デッドロックはいくつかの理由で発生する可能性があるため、独自のロックシステムを構築する前にそれらの理由を調査することをお勧めします。システムヘルスXEセッションからデッドロックグラフキャプチャして、それらの分析を開始できます。

通常、デッドロックは異なる順序でオブジェクトをロックした結果です。そのため、常に同じ順序でロックを取得するようにしてください。デッドロックが発生する理由は他にもありますが、一般的なガイダンスは、短いトランザクションが短時間ロックを保持するため、より良いコード、インデックス、およびdivide-et-impera戦略でクエリを調整することは、おそらく取り除くための最良の方法ですデッドロックの。

「リーダーがライターをブロックする」種類のデッドロックは、データベースをRead Committed Snapshot Isolationに切り替えることで大幅に軽減できます。アプリケーションが悲観的ロックを念頭に置いて構築されている場合は、データベースでこのオプションをアクティブにする前に、すべてを慎重に確認およびテストする必要があります。

「カスタムロックシステム」のルートをたどる場合は、アプリで何か問題が発生した場合にロックを確実に保証できるものを使用してください。組み込みのsp_getapplockストアドプロシージャを調査したい場合があります。

更新:編集した質問を読んだ後、これは同じクエリを表現する別の方法です:

SELECT *
FROM (
    SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY name ORDER BY id DESC) 
    FROM locks
) AS data
WHERE RN = 1 
    AND lock = 1;

オブジェクトのロックが1回だけ許可されている場合に機能します。これは、とにかくロックシステムの要点のようです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.