13

これには説明が必要です。私が行ったことは、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 を取得し、同時にそれを確認しようとすることはできませんでしたか? 別のストアド プロシージャがクエリしているメッセージを別のストアド プロシージャが確認できないようにするために、ある種のロックを実装できますか (または実装する必要がありますか)。

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

4

7 に答える 7

7

このようなもの

--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
...
...
于 2008-09-11T21:09:57.000 に答える
2

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

その句が必要かどうかを言うために、SQL Serverの自動ロックに関するより深い知識は言うまでもなくActionMessagesUnacknowledgedDemandsテーブル(ビュー/ TVFなど)に関する詳細情報が必要です。and AcknowledgedTime is nullサブセレクトと更新の間の競合状態を防ぐためにあります。ActionMessagesそれ自体から選択する場合 (たとえば、サブ選択 on の代わりにonwhere AcknowledgedTime is nullを使用する場合)は必要ないことは確かです。不要であっても無害だと思います。topupdateUnacknowledgedDemands

OUTPUTSQL Server 2005 以降にあることに注意してください。それはあなたが使用していたとあなたが言ったことですが、古い SQL Server 2000 インストールとの互換性が必要な場合は、別の方法を使用することをお勧めします。

于 2011-07-19T11:25:43.490 に答える
1

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 */

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

于 2008-09-24T12:59:45.607 に答える
1

@キルホッファー:

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

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

于 2008-09-11T21:06:23.327 に答える
0

本当に1つずつ処理する必要がありますか? 今日の日付で未確認のメッセージをすべて SQL Server に確認させ、それらを返すようにすべきではありませんか? (もちろんすべて取引でも)

于 2008-09-11T21:04:32.723 に答える
0

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

于 2008-09-11T21:11:06.763 に答える
-1

コードをトランザクションでラップすると、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
...
于 2008-09-11T21:01:47.907 に答える