Escalada de bloqueo: ¿qué está pasando aquí?

138

Al alterar una tabla (eliminar una columna) en SQL Server 2008, hice clic en el botón Generar secuencia de comandos de cambio y noté que la secuencia de comandos de cambio que generó deja caer la columna, dice "ir" y luego ejecuta una instrucción ALTER TABLE adicional que parece establecer la escala de bloqueo de la tabla a "TABLE". Ejemplo:

ALTER TABLE dbo.Contract SET (LOCK_ESCALATION = TABLE)

También debo señalar que esto es lo último que está haciendo el script de cambio. ¿Qué está haciendo aquí y por qué está configurando LOCK_ESCALATION en TABLE?

James Alexander
fuente

Respuestas:

165

" Bloqueo de escala " es cómo SQL maneja el bloqueo para actualizaciones grandes. Cuando SQL va a cambiar muchas filas, es más eficiente que el motor de la base de datos tome menos bloqueos más grandes (por ejemplo, toda la tabla) en lugar de bloquear muchas cosas más pequeñas (por ejemplo, bloqueos de filas).

Pero esto puede ser problemático cuando tiene una tabla enorme, porque bloquear una tabla completa puede bloquear otras consultas durante mucho tiempo. Esa es la compensación: muchos bloqueos de granularidad pequeña son más lentos que menos (o uno) bloqueos de grano grueso, y tener múltiples consultas bloqueando diferentes partes de una tabla crea la posibilidad de un punto muerto si un proceso está esperando a otro.

Hay una opción de nivel de tabla LOCK_ESCALATION, nueva en SQL 2008, que permite el control de la escalada de bloqueo. El valor predeterminado "TABLE" permite que los bloqueos se escalen hasta el nivel de la tabla. DISABLE evita la escalada de bloqueo a toda la tabla en la mayoría de los casos. AUTO permite bloqueos de tabla, excepto si la tabla está particionada, en cuyo caso los bloqueos solo se hacen hasta el nivel de partición. Vea esta publicación de blog para más información.

Sospecho que el IDE agrega esta configuración al volver a crear una tabla porque TABLE es el valor predeterminado en SQL 2008. Tenga en cuenta que LOCK_ESCALATION no es compatible con SQL 2005, por lo que deberá quitarlo si intenta ejecutar el script en un Instancia de 2005. Además, dado que TABLE es el valor predeterminado, puede eliminar esa línea de forma segura cuando vuelva a ejecutar su script.

También tenga en cuenta que, en SQL 2005 antes de que esta configuración estuviera presente, todos los bloqueos podían escalar al nivel de la tabla; en otras palabras, "TABLE" era la única configuración en SQL 2005.

Justin Grant
fuente
1
Publicación duplicada en los foros de MSDN también: social.msdn.microsoft.com/Forums/en-US/sqldatabaseengine/thread/…
Jonathan Kehayias
66
@dma_k: esta opción no es relevante CREATE TABLEporque la tabla aún no existe, por lo que no hay nada que bloquear.
Justin Grant
1
Pero, ¿por qué es la instrucción LOCK_ESCALATION después de la instrucción ALTER TABLE inicial en el script de cambio al diseñar una tabla en SSMS? Seguramente para entonces el trabajo ya está hecho. ¿No debería ser antes de cambiar la estructura de la tabla?
Ingeniero invertido
2
@DaveBoltman: el SET forma parte de la instrucción ALTER TABLE. No es una declaración separada. Ver docs.microsoft.com/en-us/sql/t-sql/statements/…
Justin Grant
2
JustinGrant, aún así, la pregunta de @DaveBoltman se mantiene. El script que genera SSMS para, por ejemplo, agregar una nueva columna tiene dos ALTER TABLEdeclaraciones separadas . Primero ALTER TABLE ADD column, luego GO, luego segundo ALTER TABLE SET LOCK_ESCALATION=TABLE, luego segundo GO. Entonces, LOCK_ESCALATIONse establece después de agregar la columna. ¿Cuál es el punto de configurarlo después del hecho? Estas dos ALTER TABLEdeclaraciones están envueltas en una transacción, pero aún así la columna se agrega antes de que LOCK_ESCALATIONse establezca. Creo que profundizaré un poco más y escribiré otra respuesta.
Vladimir Baranov
11

Puede verificar si necesita incluir la declaración LOCK_ESCALATION en su script comparando este valor antes y después de ejecutar la parte principal de su script:

SELECT lock_escalation_desc FROM sys.tables WHERE name='yourtablename'

En mi caso, alterar la tabla para eliminar o agregar una restricción no parece modificar este valor.

Bogdan Verbenets
fuente
11

La respuesta de Justin Grant explica qué LOCK_ESCALATIONhace la configuración en general, pero pierde un detalle importante y no explica por qué SSMS genera el código que lo establece. Especialmente, parece muy extraño que LOCK_ESCALATIONse establezca como una última declaración en el script.

Hice algunas pruebas y aquí está mi comprensión de lo que está sucediendo aquí.

Version corta

La ALTER TABLEdeclaración que agrega, elimina o altera una columna implícitamente toma un bloqueo de modificación de esquema (SCH-M) en la tabla, que no tiene nada que ver con la LOCK_ESCALATIONconfiguración de una tabla. LOCK_ESCALATIONafecta el comportamiento de bloqueo durante las instrucciones DML ( INSERT, UPDATE, DELETE, etc.), no durante las sentencias DDL ( ALTER). El bloqueo SCH-M siempre es un bloqueo de todo el objeto de la base de datos, tabla en este ejemplo.

Esto es probablemente de donde viene la confusión.

SSMS agrega la ALTER TABLE <TableName> SET (LOCK_ESCALATION = ...)declaración a su script en todos los casos, incluso cuando no es necesario. En los casos en que se necesita esta declaración, se agrega para preservar la configuración actual de la tabla, no para bloquear la tabla de alguna manera específica durante el cambio en el esquema de la tabla que ocurre en ese script.

En otras palabras, la tabla se bloquea con el bloqueo SCH-M en la primera ALTER TABLE ALTER COLUMNinstrucción mientras se realiza todo el trabajo de cambiar el esquema de la tabla. La última ALTER TABLE SET LOCK_ESCALATIONdeclaración no lo afecta. Afecta a las declaraciones DML futuro sólo ( INSERT, UPDATE, DELETE, etc.) para esa tabla.

A primera vista, parece que SET LOCK_ESCALATION = TABLEtiene algo que ver con el hecho de que estamos cambiando toda la tabla (estamos alterando su esquema aquí), pero es engañoso.

Versión larga

Cuando se modifica la tabla en algunos casos, SSMS genera un script que recrea la tabla completa y en algunos casos más simples (como agregar o soltar una columna) el script no vuelve a crear la tabla.

Tomemos esta tabla de muestra como ejemplo:

CREATE TABLE [dbo].[Test](
    [ID] [int] NOT NULL,
    [Col1] [nvarchar](50) NOT NULL,
    [Col2] [int] NOT NULL,
 CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

Cada tabla tiene una LOCK_ESCALATIONconfiguración, que está configurada TABLEde manera predeterminada. Vamos a cambiarlo aquí:

ALTER TABLE dbo.Test SET (LOCK_ESCALATION = DISABLE)

Ahora, si trato de cambiar el Col1tipo en el diseñador de tablas SSMS, SSMS genera un script que recrea la tabla completa:

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_Test
    (
    ID int NOT NULL,
    Col1 nvarchar(10) NOT NULL,
    Col2 int NOT NULL
    )  ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_Test SET (LOCK_ESCALATION = DISABLE)
GO
IF EXISTS(SELECT * FROM dbo.Test)
     EXEC('INSERT INTO dbo.Tmp_Test (ID, Col1, Col2)
        SELECT ID, CONVERT(nvarchar(10), Col1), Col2 FROM dbo.Test WITH (HOLDLOCK TABLOCKX)')
GO
DROP TABLE dbo.Test
GO
EXECUTE sp_rename N'dbo.Tmp_Test', N'Test', 'OBJECT' 
GO
ALTER TABLE dbo.Test ADD CONSTRAINT
    PK_Test PRIMARY KEY CLUSTERED 
    (
    ID
    ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO
COMMIT

Puede ver arriba que se establece LOCK_ESCALATIONpara la tabla recién creada. SSMS lo hace para preservar la configuración actual de la tabla. SSMS genera esta línea, incluso si el valor actual de la configuración es el TABLEvalor predeterminado . Solo para ser seguro y explícito y evitar posibles problemas futuros si en el futuro este valor predeterminado cambia, supongo. Esto tiene sentido.

En este ejemplo, es realmente necesario generar la SET LOCK_ESCALATIONdeclaración, porque la tabla se crea de nuevo y su configuración debe conservarse.

Si trato de hacer un cambio simple en la tabla usando el diseñador de tablas SSMS, como agregar una nueva columna, entonces SSMS genera un script que no vuelve a crear la tabla:

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.Test ADD
    NewCol nchar(10) NULL
GO
ALTER TABLE dbo.Test SET (LOCK_ESCALATION = DISABLE)
GO
COMMIT

Como puede ver, todavía agrega la ALTER TABLE SET LOCK_ESCALATIONdeclaración, aunque en este caso no es necesaria en absoluto. El primero ALTER TABLE ... ADDno cambia la configuración actual. Supongo que los desarrolladores de SSMS decidieron que no vale la pena intentar determinar en qué casos estoALTER TABLE SET LOCK_ESCALATION declaración es redundante y generarla siempre, solo para estar seguros. No hay daño en agregar esta declaración cada vez.

Una vez más, la LOCK_ESCALATIONconfiguración de toda la tabla es irrelevante, mientras que el esquema de la tabla cambia a través de la ALTER TABLEinstrucción. LOCK_ESCALATIONla configuración solo afecta el comportamiento de bloqueo de las declaraciones DML, comoUPDATE .

Finalmente, una cita de ALTER TABLE, enfatiza la mía:

Los cambios especificados en ALTER TABLE se implementan de inmediato. Si los cambios requieren modificaciones de las filas de la tabla, ALTER TABLE actualiza las filas. ALTER TABLE adquiere un bloqueo de modificación de esquema (SCH-M) en la tabla para asegurarse de que ninguna otra conexión haga referencia incluso a los metadatos de la tabla durante el cambio, excepto las operaciones de índice en línea que requieren un bloqueo SCH-M muy corto al final. En una operación ALTER TABLE ... SWITCH, el bloqueo se adquiere en las tablas de origen y destino. Las modificaciones realizadas en la tabla se registran y son completamente recuperables. Los cambios que afectan a todas las filas en tablas muy grandes, como soltar una columna o, en algunas ediciones de SQL Server, agregar una columna NO NULL con un valor predeterminado, pueden tardar mucho tiempo en completarse y generar muchos registros. Estas instrucciones ALTER TABLE deben ejecutarse con el mismo cuidado que cualquier instrucción INSERT, UPDATE o DELETE que afecte a muchas filas.

Vladimir Baranov
fuente