Cómo obtener la inserción y / o actualización de SQL para no bloquear toda la tabla en MS SQL Server


13

Es un novato en el trabajo de DB, así que aprecia tu paciencia con una pregunta básica. Estoy ejecutando SQL Server 2014 en mi máquina local, y tengo una pequeña tabla y una aplicación cliente básica para probar diferentes enfoques. Me estoy poniendo lo que parece ser un bloqueo de tabla durante tanto INSERT INTOy UPDATEdeclaraciones. El cliente es una aplicación ASP.NET con el siguiente código:

OleDbConnection cn = new OleDbConnection("Provider=SQLNCLI11; server=localhost\\SQLEXPRESS; Database=<my db>; user id=<my uid>; password=<my pwd>");
cn.Open();
OleDbTransaction tn = cn.BeginTransaction();
OleDbCommand cmd = new OleDbCommand("INSERT INTO LAYOUTSv2 (LAYOUTS_name_t, LAYOUTS_enabled_b, LAYOUTS_data_m) VALUES ('name', '-1', 'data')", cn, tn);
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT SCOPE_IDENTITY()";
int newkey = Decimal.ToInt32((decimal)cmd.ExecuteScalar());
Console.WriteLine("Created index " + newkey);
Thread.Sleep(15000);
tn.Commit();
tn = cn.BeginTransaction();
cmd.CommandText = "UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key='" + newkey + "'";
cmd.Transaction = tn;
cmd.ExecuteNonQuery();
Console.WriteLine("updated row");
Thread.Sleep(15000);
tn.Rollback();
cn.Close();

Ejecuto este código, luego desde el estudio de administración que ejecuto SELECT * FROM LAYOUTSv2. Durante ambos casos, cuando el hilo del cliente está en pausa (es decir, antes de la confirmación / reversión), la consulta SELECT se bloquea hasta que se produce la confirmación / reversión.

La tabla tiene el campo LAYOUTS_key asignado como clave principal. En la ventana de propiedades muestra que es único y está agrupado, con bloqueos de página y bloqueos de fila permitidos. La configuración de escalado de bloqueo para la tabla es Desactivar ... He probado las otras configuraciones disponibles de Tabla y AUTO sin cambios. Lo intenté SELECT ... WITH (NOLOCK)y eso arroja un resultado de inmediato, pero como está bien advertido aquí y en otros lugares, no es lo que debería estar haciendo. He intentado poner la ROWLOCKpista en las declaraciones INSERTy UPDATE, pero nada ha cambiado.

El comportamiento que estoy buscando es el siguiente: antes de confirmar un INSERT, las consultas de otros subprocesos leen todas las filas, excepto la que se está INSERTeditando. Antes de confirmar una UPDATEconsulta de otros hilos, lea la versión inicial de la fila que se está UPDATEeditando. ¿Hay alguna manera de que pueda hacer esto? Si necesito proporcionar otra información para aclarar mi caso de uso, hágamelo saber. Gracias.


3
Por cierto, WHERE LAYOUTS_key='" + newkey + "'es un no-no completo por varias razones, incluida la inyección de SQL, debe usar consultas parametrizadas.
Martin Smith

1
@ MartinSmith Gracias por el aviso sobre esto ... nunca he oído hablar de consultas parametrizadas o ataques de inyección SQL.
John Riehl

@JohnRiehl, re: ataques de inyección, imagina si tu usuario establece newkey" something';DELETE FROM LAYOUTSv2 --". Su actualización se completará con éxito y luego vaciará la tabla porque el usuario manipuló la consulta insertando un apóstrofe. Normalmente, una consulta parametrizada se parece a algo así UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key=?, después de lo cual asigna valores por separado al ?(parámetro) en su código.
Daniel Hutmacher

Respuestas:


10

Lo más probable es que no esté bloqueando la "mesa completa".

Está bloqueando una fila en la tabla, pero SELECT * FROM LAYOUTSv2intenta leer toda la tabla, por lo que necesariamente está bloqueado por ese bloqueo.

Para el caso de inserción, simplemente puede especificar la READPASTpista para saltar más allá de la fila bloqueada; sin embargo, eso no dará el resultado deseado para el UPDATEcaso (saltará la fila nuevamente y no leerá la versión inicial de la fila).

Si configura la base de datos para el aislamiento de instantáneas confirmadas por lectura, esto le dará el efecto deseado para ambos casos (a expensas de un mayor uso de tempdb)


Cambié "Se lee la instantánea comprometida activada" a Verdadero y ahora funciona perfectamente sin necesidad de sugerencias. ¡Gracias! Un seguimiento ... Dejé "Permitir aislamiento de instantáneas" en Falso ... ¿está bien? Gracias.
John Riehl

@JohnRiehl: Sí, si no está utilizando explícitamente el SNAPSHOTaislamiento mejor para dejarlo deshabilitado y luego habilitarlo si posteriormente decide que esto sería útil para usted.
Martin Smith

7

Se supone que las instrucciones de inserción y actualización crean bloqueos a nivel de fila. Sin embargo, cuando el número de bloqueos en cualquier transacción es de 5,000 o más, se produce una escalada de bloqueo y se crea un bloqueo a nivel de tabla. Por favor ver más abajo.

https://technet.microsoft.com/en-us/library/ms184286(v=sql.105).aspx


No es relevante para esta pregunta ya que las declaraciones INSERT y UPDATE están escribiendo una sola fila
Martin Smith,
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.