Nesta página, descrevemos como usar a cláusula FOR UPDATE
no isolamento serializável.
O mecanismo de bloqueio da cláusula FOR UPDATE
é diferente para
leitura repetível e
isolamento serializável. Usando o isolamento serializável, quando você usa a consulta SELECT
para verificar uma tabela, adicionar uma cláusula FOR UPDATE
ativa bloqueios exclusivos na interseção do nível de granularidade de linha e coluna, também conhecido como nível de célula. O bloqueio permanece no lugar durante toda a transação de leitura/gravação. Durante esse período, a cláusula FOR UPDATE
impede que outras transações modifiquem as células bloqueadas até que a transação atual seja concluída.
Para saber como usar a cláusula FOR UPDATE
, consulte os guias de referência do GoogleSQL e do PostgreSQL
FOR UPDATE
.
Por que usar a cláusula FOR UPDATE
Em bancos de dados com níveis de isolamento menos rigorosos, a cláusula FOR UPDATE
pode ser
necessária para garantir que uma transação simultânea não atualize os dados entre
a leitura dos dados e a confirmação da transação. Como o Spanner impõe a serialização por padrão, é garantido que a transação só será confirmada se os dados acessados nela não estiverem desatualizados no momento da confirmação. Portanto, a cláusula FOR UPDATE
não é necessária para garantir a correção da transação no Spanner.
No entanto, em casos de uso com alta contenção de gravação, como quando várias transações estão lendo e gravando simultaneamente os mesmos dados, as transações simultâneas podem causar um aumento nos cancelamentos. Isso acontece porque quando várias transações simultâneas adquirem bloqueios compartilhados e tentam fazer upgrade para bloqueios exclusivos, elas causam um impasse. O impasse bloqueia permanentemente as transações porque cada uma está esperando que a outra libere o recurso necessário. Para progredir, o Spanner cancela todas as transações, exceto uma, para resolver o deadlock. Para mais informações, consulte Bloqueio.
Uma transação que usa a cláusula FOR UPDATE
adquire o bloqueio exclusivo
de forma proativa e continua a ser executada, enquanto outras transações aguardam a vez
para o bloqueio. Embora o Spanner ainda possa limitar a capacidade
porque as transações conflitantes só podem ser realizadas uma de cada vez, mas
como o Spanner só está fazendo progresso em uma transação, ele
economiza tempo que seria gasto cancelando e tentando as transações de novo.
Portanto, se for importante reduzir o número de transações anuladas em um cenário de solicitação de gravação simultânea, use a cláusula FOR UPDATE
para reduzir o número geral de anulações e aumentar a eficiência da execução da carga de trabalho.
Comparação com a dica LOCK_SCANNED_RANGES
A cláusula FOR UPDATE
tem uma função semelhante à dica
LOCK_SCANNED_RANGES=exclusive
.
Há duas diferenças principais:
Se você usar a dica
LOCK_SCANNED_RANGES
, a transação vai adquirir bloqueios exclusivos nos intervalos verificados para toda a instrução. Não é possível adquirir bloqueios exclusivos em uma subconsulta. Usar a dica de bloqueio pode resultar na aquisição de mais bloqueios do que o necessário e contribuir para a disputa de bloqueios na carga de trabalho. O exemplo a seguir mostra como usar uma dica de bloqueio:@{lock_scanned_ranges=exclusive} SELECT s.SingerId, s.FullName FROM Singers AS s JOIN (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000) AS a ON a.SingerId = s.SingerId;
Por outro lado, você pode usar a cláusula
FOR UPDATE
em uma subconsulta, conforme mostrado no exemplo a seguir:SELECT s.SingerId, s.FullName FROM Singers AS s JOIN (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000) FOR UPDATE AS a ON a.SingerId = s.SingerId;
É possível usar a dica
LOCK_SCANNED_RANGES
em instruções DML, mas só é possível usar a cláusulaFOR UPDATE
em instruçõesSELECT
.
Semântica de bloqueio
Para reduzir as solicitações de gravação simultâneas e o custo das transações anuladas como resultado de um deadlock, o Spanner bloqueia os dados no nível da célula, se possível. O nível de célula é o mais granular de dados em uma tabela: um ponto de dados na interseção de uma linha e uma coluna. Ao usar a cláusula FOR UPDATE
, o Spanner bloqueia células específicas que são verificadas pela consulta SELECT
.
No exemplo a seguir, a célula MarketingBudget
na linha SingerId = 1
e
AlbumId = 1
é bloqueada exclusivamente na tabela Albums
, impedindo que
transações simultâneas modifiquem essa célula até que a transação seja
confirmada ou revertida. No entanto, transações simultâneas ainda podem atualizar a célula AlbumTitle
nessa linha.
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId = 1
FOR UPDATE;
Transações simultâneas podem ser bloqueadas ao ler dados bloqueados
Quando uma transação adquire bloqueios exclusivos em um intervalo verificado, transações simultâneas podem bloquear a leitura desses dados. O Spanner impõe a serialização para que os dados só possam ser lidos se houver garantia de que eles não serão alterados por outra transação durante o ciclo de vida da transação. Transações simultâneas que tentam ler dados já bloqueados podem precisar esperar até que a transação que mantém os bloqueios seja confirmada, revertida ou atinja o tempo limite.
No exemplo a seguir, Transaction 1
bloqueia as células MarketingBudget
para
1 <= AlbumId < 5
.
-- Transaction 1
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 5
FOR UPDATE;
Transaction 2
, que está tentando ler o MarketingBudget
para
AlbumId = 1
, fica bloqueado até que Transaction 1
seja confirmado ou revertido.
-- Transaction 2
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId = 1;
-- Blocked by Transaction 1
Da mesma forma, uma transação que tenta bloquear um intervalo verificado com FOR UPDATE
é
bloqueada por uma transação simultânea que bloqueia um intervalo verificado sobreposto.
Transaction 3
no exemplo a seguir também está bloqueado porque Transaction 1
bloqueou as células MarketingBudget
para 3 <= AlbumId < 5
, que é o
intervalo verificado sobreposto com Transaction 3
.
-- Transaction 3
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 3 and AlbumId < 10
FOR UPDATE;
-- Blocked by Transaction 1
Ler um índice
Uma leitura simultânea pode não ser bloqueada se a consulta que bloqueou o intervalo verificado bloquear as linhas na tabela base, mas a transação simultânea ler de um índice.
O Transaction 1
a seguir bloqueia as células SingerId
e SingerInfo
para
SingerId = 1
.
-- Transaction 1
SELECT SingerId, SingerInfo
FROM Singers
WHERE SingerId = 1
FOR UPDATE;
O Transaction 2
somente leitura não é bloqueado pelos bloqueios adquiridos em
Transaction 1
, porque ele consulta uma tabela de índice.
-- Transaction 2
SELECT SingerId FROM Singers;
Transações simultâneas não bloqueiam operações DML em dados já bloqueados
Quando uma transação adquire bloqueios em um intervalo de células com uma dica de bloqueio exclusivo, as transações simultâneas que tentam realizar uma gravação sem ler os dados primeiro nas células bloqueadas podem continuar. A transação é bloqueada na confirmação até que a transação que mantém os bloqueios seja confirmada ou revertida.
O Transaction 1
a seguir bloqueia as células MarketingBudget
para
1 <= AlbumId < 5
.
-- Transaction 1
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 5
FOR UPDATE;
Se Transaction 2
tentar atualizar a tabela Albums
, isso será bloqueado até que Transaction 1
confirme ou reverta.
-- Transaction 2
UPDATE Albums
SET MarketingBudget = 200000
WHERE SingerId = 1 and AlbumId = 1;
> Query OK, 1 rows affected
COMMIT;
-- Blocked by Transaction 1
As linhas e lacunas atuais são bloqueadas quando um intervalo verificado é bloqueado.
Quando uma transação adquire bloqueios exclusivos em um intervalo verificado, transações simultâneas não podem inserir dados nas lacunas desse intervalo.
O Transaction 1
a seguir bloqueia as células MarketingBudget
para
1 <= AlbumId < 10
.
-- Transaction 1
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 10
FOR UPDATE;
Se Transaction 2
tentar inserir uma linha para AlbumId = 9
que ainda não existe, isso será bloqueado até que Transaction 1
faça commit ou rollback.
-- Transaction 2
INSERT INTO Albums (SingerId, AlbumId, AlbumTitle, MarketingBudget)
VALUES (1, 9, "Hello hello!", 10000);
> Query OK, 1 rows affected
COMMIT;
-- Blocked by Transaction 1
Advertências de aquisição de bloqueio
A semântica de bloqueio descrita fornece orientação geral, mas não garante exatamente como os bloqueios podem ser adquiridos quando o Spanner executa uma transação que usa a cláusula FOR UPDATE
. Os mecanismos de otimização de consultas do Spanner também podem afetar quais bloqueios são adquiridos. A cláusula
impede que outras transações modifiquem as células bloqueadas até que a transação
atual seja concluída.
Sintaxe das consultas
Esta seção fornece orientações sobre a sintaxe de consulta ao usar a cláusula FOR UPDATE
.
O uso mais comum é em uma instrução SELECT
de nível superior. Exemplo:
SELECT SingerId, SingerInfo
FROM Singers WHERE SingerID = 5
FOR UPDATE;
Este exemplo mostra como usar a cláusula FOR UPDATE
em uma instrução SELECT
para bloquear exclusivamente as células SingerId
e SingerInfo
de
WHERE SingerID = 5
.
Uso em instruções WITH
A cláusula FOR UPDATE
não adquire bloqueios para a instrução WITH
quando
você especifica FOR UPDATE
na consulta de nível externo da instrução WITH
.
Na consulta a seguir, nenhum bloqueio é adquirido pela tabela Singers
porque
a intenção de bloquear não é propagada para a consulta de expressões de tabela comuns (CTE).
WITH s AS (SELECT SingerId, SingerInfo FROM Singers WHERE SingerID > 5)
SELECT * FROM s
FOR UPDATE;
Se a cláusula FOR UPDATE
for especificada na consulta de CTE, o intervalo verificado da consulta de CTE vai adquirir os bloqueios.
No exemplo a seguir, as células SingerId
e SingerInfo
das linhas em que SingerId > 5
estão bloqueadas.
WITH s AS
(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5 FOR UPDATE)
SELECT * FROM s;
Uso em subconsultas
É possível usar a cláusula FOR UPDATE
em uma consulta de nível externo que tenha uma ou mais subconsultas. Os bloqueios são adquiridos pela consulta de nível superior e dentro de subconsultas, exceto em subconsultas de expressão.
A consulta a seguir bloqueia as células SingerId
e SingerInfo
para linhas em que
SingerId > 5.
(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5) AS t
FOR UPDATE;
A consulta a seguir não bloqueia nenhuma célula na tabela Albums
porque está
em uma subconsulta de expressão. As células SingerId
e SingerInfo
das linhas retornadas pela subconsulta de expressão são bloqueadas.
SELECT SingerId, SingerInfo
FROM Singers
WHERE SingerId = (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000)
FOR UPDATE;
Usado para consultar visualizações
Você pode usar a cláusula FOR UPDATE
para consultar uma visualização, conforme mostrado no exemplo a seguir:
CREATE VIEW SingerBio AS SELECT SingerId, FullName, SingerInfo FROM Singers;
SELECT * FROM SingerBio WHERE SingerId = 5 FOR UPDATE;
Não é possível usar a cláusula FOR UPDATE
ao definir uma visualização.
Casos de uso não aceitos
Os seguintes casos de uso do FOR UPDATE
não são compatíveis:
- Como um mecanismo de exclusão mútua para execução de código fora do Spanner:não use o bloqueio no Spanner para garantir acesso exclusivo a um recurso fora dele. As transações podem ser canceladas pelo Spanner, por exemplo, se uma transação for repetida, seja explicitamente pelo código do aplicativo ou implicitamente pelo código do cliente, como o driver JDBC do Spanner, só é garantido que os bloqueios sejam mantidos durante a tentativa que foi executada.
- Em combinação com a dica
LOCK_SCANNED_RANGES
:não é possível usar a cláusulaFOR UPDATE
e a dicaLOCK_SCANNED_RANGES
na mesma consulta. Caso contrário, o Spanner vai retornar um erro. - Em consultas de pesquisa de texto completo:não é possível usar a cláusula
FOR UPDATE
em consultas que usam índices de pesquisa de texto completo. - Em transações somente leitura:a cláusula
FOR UPDATE
só é válida em consultas executadas em transações de leitura e gravação. - Em instruções DDL:não é possível usar a cláusula
FOR UPDATE
em consultas dentro de instruções DDL, que são armazenadas para execução posterior. Por exemplo, não é possível usar a cláusulaFOR UPDATE
ao definir uma visualização. Se o bloqueio for necessário, a cláusulaFOR UPDATE
poderá ser especificada ao consultar a visualização.
A seguir
- Saiba como usar a cláusula
FOR UPDATE
no GoogleSQL e no PostgreSQL. - Saiba como usar SELECT FOR UPDATE no isolamento de leitura repetível.
- Saiba mais sobre a dica
LOCK_SCANNED_RANGES
. - Saiba mais sobre o bloqueio no Spanner.
- Saiba mais sobre a capacidade de serialização do Spanner.