sql >> Base de Datos >  >> RDS >> Sqlserver

En tsql, ¿es seguro insertar con una instrucción Select en términos de concurrencia?

Como escribe Paul:No, no es seguro , para lo cual me gustaría agregar evidencia empírica:Crear una tabla Table_1 con un campo ID y un registro con valor 0 . Luego ejecute el siguiente código simultáneamente en dos ventanas de consulta de Management Studio :

declare @counter int
set @counter = 0
while @counter < 1000
begin
  set @counter = @counter + 1

  INSERT INTO Table_1
    SELECT MAX(ID) + 1 FROM Table_1 

end

Luego ejecuta

SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1

En mi SQL Server 2008, una ID (662 ) se creó dos veces. Por lo tanto, el nivel de aislamiento predeterminado que se aplica a declaraciones individuales es no suficiente.

EDITAR:Claramente, envolviendo el INSERT con BEGIN TRANSACTION y COMMIT no lo arreglará, ya que el nivel de aislamiento predeterminado para las transacciones sigue siendo READ COMMITTED , que no es suficiente. Tenga en cuenta que establecer el nivel de aislamiento de transacciones en REPEATABLE READ es también insuficiente. La única forma de hacer que el código anterior sea seguro es agregar

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

en la cima. Sin embargo, esto provocó bloqueos de vez en cuando en mis pruebas.

EDITAR:La única solución que encontré que es segura y no produce interbloqueos (al menos en mis pruebas) es bloquear explícitamente la tabla exclusivamente (el nivel de aislamiento de transacción predeterminado es suficiente aquí). Sin embargo, ten cuidado; esta solución podría matar rendimiento:

...loop stuff...
    BEGIN TRANSACTION

    SELECT * FROM Table_1 WITH (TABLOCKX, HOLDLOCK) WHERE 1=0

    INSERT INTO Table_1
      SELECT MAX(ID) + 1 FROM Table_1 

    COMMIT
...loop end...