Come si fa a bloccare le tabelle in SQL Server 2005, e devo ancora farlo?
-
09-06-2019 - |
Domanda
Questo prenderà qualche spiegazione.Quello che ho fatto è creare uno specifico messaggio personalizzato coda in SQL Server 2005.Ho una tabella con i messaggi che contengono i timestamp per entrambi riconoscimento e di completamento.La stored procedure che i chiamanti eseguire per ottenere il messaggio successivo nella coda di stampa, inoltre, riconosce il messaggio.Finora tutto bene.Beh, se il sistema sta vivendo una quantità enorme di transazioni (in migliaia al minuto), non è possibile che un messaggio per essere riconosciuto da un'altra esecuzione della stored procedure, mentre un altro è preparato a ciò?Mi permetta di aiutare mostrando il mio codice SQL nella stored proc:
--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
...
...
Nel codice di cui sopra, non un'altra stored procedure in esecuzione allo stesso tempo di ottenere lo stesso id e il tentativo di riconoscere allo stesso tempo?Posso (o devo) implementare una sorta di blocco per impedire un'altra stored proc dal riconoscere i messaggi che un'altra stored proc è la ricerca?
Wow, qualcuno di questo anche il senso?E 'un po' difficile mettere a parole...
Soluzione
Qualcosa di simile a questo
--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
...
...
Altri suggerimenti
Questo mi sembra il tipo di situazione in cui OUTPUT
può essere utile:
-- 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)
...
...
Ci aggiorniamo e afferrare la riga nella stessa operazione, che racconta la query optimizer esattamente quello che stiamo facendo, permettendo di scegliere la più granulare di blocco è possibile e mantenere per il più breve tempo possibile.(Anche se la colonna è prefisso INSERTED
, OUTPUT
è come trigger, espressa in termini di UPDATE
come eliminare la riga e inserire quello nuovo).
Avrei bisogno di più informazioni circa il vostro ActionMessages
e UnacknowledgedDemands
tabelle (vista/funzioni con valori di tabella/qualunque cosa), per non parlare di una maggiore conoscenza di SQL Server chiusura automatica, per dire che se and AcknowledgedTime is null
la clausola è necessario.È lì per difendere contro una condizione di competizione tra il sub-select e update.Sono certo che non sarebbe necessario se fossimo selezione da ActionMessages
stessa (ad esempio, where AcknowledgedTime is null
con un top
sul update
, invece di sub-select su UnacknowledgedDemands
).Mi aspetto anche se è inutile, è innocuo.
Nota che OUTPUT
in SQL Server 2005 e di cui sopra.Che è quello che hai detto che stavi usando, ma se la compatibilità con geriatrica SQL Server 2000 installa erano necessari, vuoi andare in un altro modo.
@Kilhoffer:
L'intero batch SQL viene analizzato prima dell'esecuzione, in modo che SQL sa che si sta andando a fare un aggiornamento della tabella e selezionare da.
Edit:Inoltre, SQL non necessariamente bloccare l'intero tavolo, potrebbe bloccare le righe necessarie.Vedere qui per una panoramica di blocco in SQL server.
Invece di esplicita di chiusura, che spesso è sfociata in SQL Server per maggiore granularità di quanto desiderato, perché non provare questo approccio:
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 */
Meno si blocca, il più alto livello di concorrenza che si hanno.
Dovrebbe davvero essere l'elaborazione di cose una per una?Non basta SQL Server riconosce tutti i messaggi non riconosciuti con la data di oggi e di restituirli?(tutti anche in una transazione di corso)
Si desidera racchiudere il codice in una transazione, quindi SQL server consente di gestire chiusura appropriato le righe o le tabelle.
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
...