SQL Server 2005 でテーブルをロックするにはどうすればよいですか? また、ロックする必要がありますか?

StackOverflow https://stackoverflow.com/questions/57625

  •  09-06-2019
  •  | 
  •  

質問

これについては少し説明が必要です。私が行ったのは、SQL Server 2005 に特定のカスタム メッセージ キューを作成することです。確認応答と完了の両方のタイムスタンプを含むメッセージを含むテーブルがあります。呼び出し元がキュー内の次のメッセージを取得するために実行するストアド プロシージャも、メッセージを確認応答します。ここまでは順調ですね。さて、システムで大量のトランザクション (1 分あたり数千件) が発生している場合、ストアド プロシージャの別の実行によってメッセージが確認応答される準備ができている間に、メッセージが確認応答される可能性はありませんか?ストアド プロシージャ内の SQL コードを示してみましょう。

--Grab the next message id
declare @MessageId uniqueidentifier
set @MessageId = (select top(1) ActionMessageId from UnacknowledgedDemands);

--Acknowledge the message
update ActionMessages
set AcknowledgedTime = getdate()
where ActionMessageId = @MessageId

--Select the entire message
...
...

上記のコードでは、同時に実行されている別のストアド プロシージャが同じ ID を取得し、同時にそれを確認しようとすることはできないでしょうか?別のストアド プロシージャがクエリしているメッセージを別のストアド プロシージャが認識できないようにするために、ある種のロックを実装できますか (実装する必要がありますか)。

うわー、これには意味があったでしょうか?言葉で言うのはちょっと難しいんですが…

役に立ちましたか?

解決

このようなもの

--Grab the next message id
begin tran
declare @MessageId uniqueidentifier
select top 1 @MessageId =   ActionMessageId from UnacknowledgedDemands with(holdlock, updlock);

--Acknowledge the message
update ActionMessages
set AcknowledgedTime = getdate()
where ActionMessageId = @MessageId

-- some error checking
commit tran

--Select the entire message
...
...

他のヒント

このような状況のようです OUTPUT 役に立つかもしれません:

-- Acknowledge and grab the next message
declare @message table (
    -- ...your `ActionMessages` columns here...
)
update ActionMessages
set    AcknowledgedTime = getdate()
output INSERTED.* into @message
where  ActionMessageId in (select top(1) ActionMessageId from UnacknowledgedDemands)
  and  AcknowledgedTime is null

-- Use the data in @message, which will have zero or one rows assuming
-- `ActionMessageId` uniquely identifies a row (strongly implied in your question)
...
...

そこで、同じ操作で行を更新して取得し、クエリ オプティマイザーに伝えます。 その通り 私たちが行っていることは、できる限り粒度の高いロックを選択し、可能な限り短時間でそれを維持できるようにすることです。(ただし、列の接頭辞は INSERTED, OUTPUT トリガーのようなもので、 UPDATE 行を削除して新しい行を挿入するようなものです。)

あなたについてもっと詳しい情報が必要です ActionMessages そして UnacknowledgedDemands テーブル (ビュー/TVF/その他)、SQL Server の自動ロックに関するより深い知識は言うまでもなく、 and AcknowledgedTime is null という条項が必要です。これは、サブ選択と更新の間の競合状態を防ぐためにあります。から選ぶなら確かに必要ないのですが ActionMessages それ自体 (例: where AcknowledgedTime is null とともに topupdate, のサブ選択の代わりに、 UnacknowledgedDemands)。たとえ不必要であっても無害であることを期待します。

ご了承ください OUTPUT SQL Server 2005 以降に存在します。それが使用しているとのことですが、高齢者の SQL Server 2000 インストールとの互換性が必要な場合は、別の方法を選択する必要があります。

@キルホッファー:

SQL バッチ全体は実行前に解析されるため、SQL はテーブルの選択だけでなくテーブルの更新も行うことを認識します。

編集:また、SQL は必ずしもテーブル全体をロックするとは限りません。必要な行のみをロックすることもできます。見る ここ SQL サーバーでのロックの概要については、「SQL Server でのロックの概要」を参照してください。

明示的なロックは、SQL Server によって希望よりも高い粒度にエスカレートされることがよくありますが、その代わりに、次のアプローチを試してみてはいかがでしょうか。

declare @MessageId uniqueidentifier
select top 1 @MessageId = ActionMessageId from UnacknowledgedDemands

update ActionMessages
  set AcknowledgedTime = getdate()
  where ActionMessageId = @MessageId and AcknowledgedTime is null

if @@rowcount > 0
  /* acknoweldge succeeded */
else
  /* concurrent query acknowledged message before us,
     go back and try another one */

ロックが少ないほど、同時実行性が高くなります。

本当に 1 つずつ処理する必要がありますか?SQL Server に今日の日付を持つすべての未確認メッセージを確認して返すようにするべきではないでしょうか。(もちろんすべてトランザクション内でも)

SQL Server 選択ロックの詳細を読む ここ そして ここ. 。SQL Server には、選択時にテーブル ロックを呼び出す機能があります。トランザクション中、テーブルには何も起こりません。トランザクションが完了すると、挿入または更新は自動的に解決されます。

コードをトランザクションでラップすると、SQL サーバーが適切な行またはテーブルのロックを処理します。

begin transaction

--Grab the next message id
declare @MessageId uniqueidentifier
set @MessageId = (select top(1) ActionMessageId from UnacknowledgedDemands);

--Acknowledge the message
update ActionMessages
set AcknowledgedTime = getdate()
where ActionMessageId = @MessageId

commit transaction

--Select the entire message
...
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top