Introdução
Os bancos de dados relacionais seguem as propriedades ACID na forma como implementam transações – Atomicidade, Consistência, Isolamento e Durabilidade. O isolamento é necessário para garantir que múltiplas transações não possam causar alterações nos dados e deixar os eventuais resultados inconsistentes. Para garantir que as operações permaneçam isoladas, o SQL Server aplica mecanismos de Locking.
Modos de bloqueio e hierarquia
O mecanismo do SQL Server para controle de simultaneidade está envolvido. Para otimizar o desempenho em termos de esperas de bloqueio, impasses e similares, você precisa tomar uma decisão com base no cenário específico.
No SQL Server, os bloqueios podem ser mantidos de várias maneiras e em vários níveis de granularidade. Modos de bloqueio são as maneiras específicas de fazer isso, e seus níveis são Hierarquia de bloqueio.
A Figura 1 mostra os modos de bloqueio disponíveis no SQL Server para o nível de isolamento de transação padrão (READ COMMITTED):
Visão geral do escalonamento de bloqueio
O SQL Server pode bloquear recursos em vários níveis. Depende dos atos mais eficientes de acordo com a natureza da carga de trabalho. A Tabela 1 mostra os recursos que podem ser bloqueados.
- Os bloqueios em um nível mais granular (por exemplo, bloqueios de nível de linha) permitem maior simultaneidade e menos bloqueio.
- Os bloqueios em um nível superior (por exemplo, bloqueio no nível da tabela) reduzem a simultaneidade. Eles podem causar mais bloqueios, dependendo de quanto tempo dura a instrução real.
O SQL Server escolhe o nível de bloqueio necessário de acordo com as métricas internas.
Um escalonamento de bloqueio ocorre quando um bloqueio é convertido de um nível mais fino de granularidade para um nível mais grosseiro.
Por exemplo, converter um bloqueio de linha em um bloqueio de tabela (consulte a Tabela 1).
Recurso | Descrição |
RID | O identificador de linha usado para bloquear uma única linha em um heap. |
CHAVE | O bloqueio de linha em um índice usado para proteger intervalos de chaves em transações serializáveis. |
PÁGINA | A página de 8 kilobytes (KB) em um banco de dados, como páginas de dados ou de índice. |
EXTENSÃO | O grupo contíguo de oito páginas, como páginas de dados ou de índice. |
HoBT | A pilha ou árvore B. O bloqueio está protegendo uma árvore B (índice) ou as páginas de dados de heap em uma tabela que não possui um índice clusterizado. |
TABELA | A tabela inteira, incluindo todos os dados e índices. |
ARQUIVO | O arquivo de banco de dados. |
APLICATIVO | O recurso especificado pelo aplicativo. |
METADADOS | Bloqueios de metadados. |
ALLOCATION_UNIT | A unidade de alocação. |
BANCO DE DADOS | Todo o banco de dados. |
A justificativa para o escalonamento de bloqueio
Bloqueios no SQL Server podem ser bastante caros. Para cada bloqueio adquirido pelo Lock Manager, o SQL Server deve reservar memória – 64 bytes ou 128 bytes. A quantidade depende se estamos lidando com um sistema de 32 bits ou 64 bits, respectivamente.
À medida que o número de bloqueios de linha em uma tabela aumenta, o SQL Server deve adquirir cada vez mais memória. Assim, outros processos estão morrendo de fome, sem memória.
Faz sentido converter bloqueios de linha e bloqueios de página em um único bloqueio de nível de tabela (objeto). Isso acontece quando o número de bloqueios para essa tabela excede 5.000.
O comprometimento ocorre quando a tabela inteira não está mais disponível para outras sessões no processo de transação.
Demonstrando o escalonamento de bloqueio
Podemos demonstrar o Lock Escalation usando o código na Listagem 1.
Vamos primeiro descrever um pouco a tabela. Produção.ProdutosI é uma tabela relativamente pequena com cerca de 7.777 linhas. Os elementos de construção são o mesmo conjunto de 77 linhas duplicadas 101 vezes. O Código na Listagem 1 consiste em três versões da mesma instrução de atualização, cada uma incluída em uma transação.
-- Listing 1: Demonstrating Lock Escalation
-- Update very few rows
BEGIN TRAN
use TSQLV4
GO
UPDATE Production.ProductsI SET unitprice='100.00'
WHERE unitprice='18.00';
ROLLBACK
-- Update a large number of rows
BEGIN TRAN
use TSQLV4
GO
UPDATE Production.ProductsI SET unitprice='100.00'
WHERE unitprice>'18.00';
ROLLBACK
-- Update over 5000 rows
BEGIN TRAN
use TSQLV4
GO
UPDATE Production.ProductsI SET unitprice='100.00';
ROLLBACK
Para maior clareza, detalharemos o conteúdo da Listagem 1.
Antes disso, vamos observar a Listagem 2 – uma consulta para exibir os bloqueios mantidos no banco de dados TSQLV4.
Nossa primeira ação é executar a Listagem 1a. Em seguida, usamos a Listagem 2 para examinar como o Lock Manager realiza o bloqueio no cenário. Executamos a Listagem 1a sem emitir a instrução rollback. Dessa forma, preservamos os bloqueios por tempo suficiente para que a consulta na Listagem 2 possa capturá-los.
-- Listing 1a: Demonstrating Lock Escalation
-- Update very few rows
BEGIN TRAN
use TSQLV4
GO
UPDATE Production.ProductsI SET unitprice='100.00'
WHERE unitprice='18.00';
ROLLBACK
-- Listing 2: Displaying Locks Held in Database TSQLV4
USE TSQLV4
GO
SELECT
resource_type
, DB_NAME (resource_database_id) database_name
--, OBJECT_NAME(resource_associated_entity_id) resource_name
, request_mode
, request_type
, request_status
, request_reference_count
, request_session_id
, resource_associated_entity_id
, OBJECT_NAME(resource_associated_entity_id) [object_name] --small obj ids
, getuser.login_name
FROM sys.dm_tran_locks
CROSS APPLY dmv.dbo.getuser(request_session_id) as getuser
WHERE DB_NAME (resource_database_id)='TSQLV4';
Quando executamos a consulta na Listagem 1a e, em seguida, verificamos os bloqueios usando a consulta na Listagem 2, o SQL Server retorna o resultado mostrado na Figura 2.
404 linhas na tabela têm unitprice='18.00' . O Lock Manager bloqueia essas linhas junto com os outros bloqueios de qualquer nível necessário. Ele traz a contagem de linhas da Figura 2 para 467.
-- Listing 1b: Demonstrating Lock Escalation
-- Update a large number of rows
BEGIN TRAN
use TSQLV4
GO
UPDATE Production.ProductsI SET unitprice='100.00'
WHERE unitprice>'18.00';
ROLLBACK
Observamos um comportamento semelhante quando executamos a consulta na Listagem 1b. Desta vez, estamos lidando com 4406 linhas. Ele reflete o número de linhas na tabela Production.ProductI com preço unitário>18,00.
-- Listing 1c: Demonstrating Lock Escalation
-- Update over 5000 rows
BEGIN TRAN
use TSQLV4
GO
UPDATE Production.ProductsI SET unitprice='100.00';
ROLLBACK
Quando avançamos e executamos o código na Listagem 1c, vemos um comportamento diferente (veja a Figura 4).
A listagem 1c tenta atualizar todas as 7777 linhas na tabela Production.ProductI. O SQL Server determina que bloquear tantas linhas não é mais eficiente para garantir o isolamento. Em vez disso, a tabela inteira está bloqueada.
Mais sobre escalonamento de bloqueio
O bloqueio da tabela implica que nenhuma outra sessão pode modificar suas linhas pela duração da transação, o que pode acontecer mesmo quando uma sessão de bloqueio não manipula todas as linhas da tabela.
Também vale a pena mencionar que outros fatores podem afetar como os bloqueios são adquiridos e escalados no SQL Server. Esses são o nível de isolamento configurado, indexação e sinalizadores de rastreamento.
Os sinalizadores de rastreamento T1211 e T1224 podem ser aplicados para desabilitar totalmente o escalonamento de bloqueio. O escalonamento de bloqueio também pode ser desabilitado e habilitado para uma tabela específica com o seguinte código:
-- Listing 5: Disable and Enable Lock Escalation
ALTER TABLE Production.ProductsI SET (LOCK_ESCALATION=DISABLE);
ALTER TABLE Production.ProductsI SET (LOCK_ESCALATION=TABLE);
Pode-se querer fazê-lo para reduzir o bloqueio associado ao bloqueio de toda a tabela. Por causa do impacto na memória, deve ser considerado em uma medida temporária.
Conclusão
O SQL Server usa o Lock Escalation para controlar o impacto de um bloqueio mais granular nos recursos do servidor. Para exibir a forma de ocorrência desses bloqueios – bloqueios de linha, bloqueios de página, bloqueios de objeto, etc. – consulte a exibição de gerenciamento dinâmico sys.dm_tran_locks. Ele fornece muitas informações sobre travamento, além do Lock Escalation.
Embora seja possível manipular o comportamento do Lock Manager, é essencial fazê-lo com muito cuidado. Também é crucial conhecer o impacto preciso no desempenho de qualquer esforço direcionado a fazer tais modificações.
Referências
- Korotkevitch, D., 2016. Pro SQL Server Internals. Flórida:Dmitri Korotkevitch
- Cenários de bloqueio usando Sys.dm_tran_locks
- Guia de bloqueio de transações e controle de versão de linha