Pregunta

Esto requerirá algunas explicaciones.Lo que hice fue crear una cola de mensajes personalizada específica en SQL Server 2005.Tengo una tabla con mensajes que contienen marcas de tiempo tanto para el reconocimiento como para la finalización.El procedimiento almacenado que ejecutan las personas que llaman para obtener el siguiente mensaje en su cola también reconoce el mensaje.Hasta ahora, todo bien.Bueno, si el sistema está experimentando una cantidad masiva de transacciones (miles por minuto), ¿no es posible que otra ejecución del procedimiento almacenado reconozca un mensaje mientras otra está preparada para hacerlo por sí misma?Déjame ayudarte mostrando mi código SQL en el proceso almacenado:

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

En el código anterior, ¿no podría otro procedimiento almacenado que se ejecute al mismo tiempo obtener la misma identificación e intentar reconocerla al mismo tiempo?¿Podría (o debería) implementar algún tipo de bloqueo para evitar que otro proceso almacenado reconozca los mensajes que otro proceso almacenado está consultando?

Vaya, ¿algo de esto tenía sentido?Es un poco difícil expresarlo con palabras...

¿Fue útil?

Solución

Algo como esto

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

Otros consejos

Éste parece el tipo de situación en la que OUTPUT puede ser útil:

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

Allí, actualizamos y tomamos la fila en la misma operación, lo que le indica al optimizador de consultas exactamente lo que estamos haciendo, permitiéndole elegir el bloqueo más granular posible y mantenerlo durante el menor tiempo posible.(Aunque el prefijo de la columna es INSERTED, OUTPUT es como desencadenantes, expresados ​​en términos de UPDATE siendo como eliminar la fila e insertar la nueva).

necesitaría más información sobre su ActionMessages y UnacknowledgedDemands tablas (vistas/TVF/lo que sea), sin mencionar un mayor conocimiento del bloqueo automático de SQL Server, para decir si eso and AcknowledgedTime is null cláusula es necesaria.Está ahí para defenderse de una condición de carrera entre la subselección y la actualización.Estoy seguro de que no sería necesario si estuviéramos seleccionando entre ActionMessages mismo (por ejemplo, where AcknowledgedTime is null con un top sobre el update, en lugar de la subselección en UnacknowledgedDemands).Espero que incluso si es innecesario, sea inofensivo.

Tenga en cuenta que OUTPUT está en SQL Server 2005 y superior.Eso es lo que dijo que estaba usando, pero si se requiriera compatibilidad con instalaciones geriátricas de SQL Server 2000, querrá ir por otro camino.

@Kilhoffer:

Todo el lote de SQL se analiza antes de la ejecución, por lo que SQL sabe que va a realizar una actualización de la tabla y también seleccionará de ella.

Editar:Además, SQL no necesariamente bloqueará toda la tabla; podría simplemente bloquear las filas necesarias.Ver aquí para obtener una descripción general del bloqueo en el servidor SQL.

En lugar de un bloqueo explícito, que a menudo SQL Server escala a una granularidad mayor de la deseada, ¿por qué no probar este enfoque?

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

Cuanto menos bloquees, mayor simultaneidad tendrás.

¿Realmente deberías procesar las cosas una por una?¿No debería simplemente hacer que SQL Server reconozca todos los mensajes no reconocidos con la fecha de hoy y los devuelva?(todo también en una transacción, por supuesto)

Lea más sobre el bloqueo de selección de SQL Server aquí y aquí.SQL Server tiene la capacidad de invocar un bloqueo de tabla en una selección.No le pasará nada a la mesa durante la transacción.Cuando se complete la transacción, cualquier inserción o actualización se resolverá por sí sola.

Desea incluir su código en una transacción, luego el servidor SQL se encargará de bloquear las filas o tablas apropiadas.

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
...
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top