Frage

Dieser wird nehmen Sie sich etwas zu erklären. Was ich getan habe, ist es, eine bestimmte benutzerdefinierte Nachrichten-Warteschlange erstellen in SQL Server 2005. Ich habe eine Tabelle mit Nachrichten, die Zeitstempel für beide Bestätigung und Abschluss enthält. Die gespeicherte Prozedur, die Anrufer auszuführen, um die nächste Nachricht in der Warteschlange zu erhalten, erkennt auch die Nachricht. So weit, ist es gut. Nun, wenn das System erfährt eine enorme Menge an Transaktionen (Tausend pro Minute), ist es nicht möglich, dass eine Nachricht von einer anderen Ausführung der gespeicherten Prozedur anerkannt werden, während ein anderer bereit ist, so sich selbst? Lassen Sie mich zeigen, meine SQL-Code in den gespeicherten proc helfen:

--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
...
...

In dem obigen Code, konnte nicht eine andere gespeicherte Prozedur zur gleichen Zeit laufen die gleiche ID erhalten und versuchen, sie zugleich zu erkennen? Könnte ich (oder sollte ich) eine Art von Sperr eine andere gespeicherte Prozedur aus Quittieren von Meldungen, die eine andere gespeicherte Prozedur abfragt zu verhindern implementieren?

Wow, hat irgendetwas davon sogar Sinn machen? Es ist ein bisschen schwer zu Worte zu fassen ...

War es hilfreich?

Lösung

So etwas

--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
...
...

Andere Tipps

Dies scheint wie die Art von Situation, in der OUTPUT nützlich sein kann, :

-- 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)
...
...

, wir aktualisieren und die Zeile in der gleichen Operation greifen, die die Abfrage-Optimierer sagt genau , was wir tun, so dass sie die detaillierteste Sperre wählen es kann und halten es für die möglichst kurze Zeit. (Obwohl die Spaltenpräfix INSERTED ist, wie OUTPUT Triggern, ausgedrückt in Bezug auf die UPDATE dergleichen ist die Zeile zu löschen und die neue eingefügt wird.)

würde ich mehr Informationen über Ihre ActionMessages und UnacknowledgedDemands Tabellen (views / TVFs / was auch immer) benötigen, kein größeres Wissen über SQL Server automatische Verriegelung zu erwähnen, zu sagen, ob die and AcknowledgedTime is null Klausel erforderlich. Es ist dort gegen eine Racebedingung zwischen dem Unter auswählen und das Update zu verteidigen. Ich bin sicher, dass es nicht notwendig wäre, wenn wir von ActionMessages Auswahl selbst (zum Beispiel where AcknowledgedTime is null mit einem top auf dem update anstelle des Sub-select auf UnacknowledgedDemands). Ich erwarte, dass selbst wenn es nicht notwendig ist, ist es harmlos.

Beachten Sie, dass OUTPUT in SQL Server 2005 und höher. Das ist, was Sie sagten, Sie verwendet haben, aber wenn die Kompatibilität mit geriatrischen SQL Server 2000 installiert erforderlich waren, würden Sie wollen einen anderen Weg gehen.

@Kilhoffer:

Das ganze SQL-Batch wird vor der Ausführung analysiert, so SQL weiß, dass Sie ein Update auf die Tabelle sowie wählen Sie aus der es tun werden.

Edit: Auch wird SQL nicht notwendigerweise die gesamte Tabelle sperren - es könnte nur die erforderlichen Zeilen sperren. Siehe hier für einen Überblick über Sperren in SQL Server.

Statt expliziter Sperren, die oft von SQL Server auf höhere Granularität eskalieren als gewünscht ist, warum nicht versuchen, nur diesen Ansatz:

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

Je weniger Sie sperren - die höhere Parallelität Sie haben

.

Wenn Sie wirklich die Verarbeitung werden die Dinge one-by-one? Sollten Sie nicht SQL Server nur haben alle unbestätigten Meldungen mit heutigem Datum bestätigen und sie zurückkehren? (Alle auch in einer Transaktion natürlich)

Lesen Sie mehr über SQL Server auswählen Locking hier und hier . SQL Server hat die Fähigkeit, eine Tabellensperre auf einer Auswahl aufzurufen. Nichts wird während der Transaktion in der Tabelle passieren. Wenn die Transaktion abgeschlossen ist, werden alle Einsätze oder Updates dann selbst lösen.

Sie wollen Ihren Code in einer Transaktion wickeln, dann SQL Server behandelt die entsprechenden Zeilen oder Tabellen sperren.

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
...
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top