Esta página descreve como usar a cláusula FOR UPDATE
no Spanner.
Ao usar a consulta SELECT
para verificar uma tabela, adicione uma cláusula FOR UPDATE
para
ativar bloqueios exclusivos no 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 duração da 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 mais, 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 rígidos, a cláusula FOR UPDATE
pode ser
necessária para garantir que uma transação simultânea não atualize dados entre
a leitura dos dados e a confirmação da transação. Como o Spanner
sempre aplica a serializabilidade, é garantido que a transação só
será confirmada se os dados acessados na transação 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 leem e gravam simultaneamente nos mesmos dados, as transações simultâneas podem causar um aumento nos cancelamentos. Isso ocorre porque, quando várias transações simultâneas adquirem bloqueios compartilhados e tentam fazer upgrade para bloqueios exclusivos, as transações causam um impasse. O Spanner, em seguida, cancela todas as transações, exceto uma. Para mais informações, consulte Bloqueio.
Uma transação que usa a cláusula FOR UPDATE
adquire o bloqueio exclusivo e
continua a ser executada, enquanto outras aguardam a vez para fazer
isso.
Embora o Spanner ainda possa limitar a capacidade porque as
transações conflitantes só podem ser realizadas uma de cada vez, como
ele só está progredindo em uma transação, ele economiza tempo
que seria gasto abortando e tentando transações de novo.
Portanto, se reduzir o número de transações abortadas em um cenário de solicitação de gravação simultânea for importante, use a cláusula FOR UPDATE
para reduzir o número geral de abortos 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 travas exclusivas em uma subconsulta. O uso da dica de bloqueio pode resultar na aquisição de mais bloqueios do que o necessário e contribuir para a contenção de bloqueio 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, é possível usar a cláusula
FOR UPDATE
em uma subconsulta, conforme mostrado neste exemplo: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 sugestão
LOCK_SCANNED_RANGES
em instruções DML, mas apenas a cláusulaFOR UPDATE
pode ser usada em instruçõesSELECT
.
Semântica de bloqueio
Para reduzir as solicitações de gravação simultâneas e o custo das transações abortadas
como resultado de um deadlock, o Spanner bloqueia os dados no nível da célula, se
possível. Ao usar a cláusula FOR UPDATE
, o Spanner bloqueia
células específicas que são verificadas pela consulta SELECT
.
No exemplo abaixo, a célula MarketingBudget
nas linhas 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, as 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 bloquear a leitura de 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 aplica a serializabilidade para que os dados só possam ser lidos se for garantido que não foram alterados por outra transação durante o ciclo de vida dela. 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 ou revertida.
No exemplo abaixo, 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;
O Transaction 2
, que está tentando ler o MarketingBudget
para
AlbumId = 1
, fica bloqueado até que o 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 digitalizado com FOR UPDATE
é
bloqueada por uma transação simultânea que bloqueia um intervalo digitalizado sobreposto.
O 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 de verificação 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 consulta uma tabela de índice.
-- Transaction 2
SELECT SingerId FROM Singers;
As transações simultâneas não bloqueiam as 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 prosseguir. A transação bloqueia no commit 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
, ela será bloqueada
até que Transaction 1
seja confirmada ou revertida.
-- 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 existentes são bloqueadas quando um intervalo digitalizado é bloqueado
Quando uma transação adquire bloqueios exclusivos em um intervalo digitalizado, as 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, ela será bloqueada até que Transaction 1
seja confirmada ou revertida.
-- 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ções gerais, 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 consulta
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.
Semântica da consulta
Esta seção fornece orientações sobre a semântica da consulta ao usar a cláusula
FOR UPDATE
.
Usar 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, nenhuma trava é adquirida pela tabela Singers
porque
a intenção de bloquear não é propagada para a consulta de expressões de tabela comum (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 CTE, o intervalo verificado
da consulta CTE vai adquirir as travas.
No exemplo abaixo, 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á
dentro de 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;
Usar para consultar visualizações
É possível usar a cláusula FOR UPDATE
para consultar uma visualização, conforme mostrado no exemplo
abaixo:
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 de FOR UPDATE
não têm suporte:
- Como um mecanismo de exclusão mútua para executar código fora do Spanner:não use o bloqueio no Spanner para garantir acesso exclusivo a um recurso fora do Spanner. As transações podem ser abortadas 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 são mantidos durante a tentativa que foi executada.
- Em combinação com a sugestão
LOCK_SCANNED_RANGES
:não é possível usar a cláusulaFOR UPDATE
e a sugestãoLOCK_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 em 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
- Aprenda a usar a cláusula
FOR UPDATE
no GoogleSQL e no PostgreSQL. - Saiba mais sobre a dica
LOCK_SCANNED_RANGES
. - Saiba mais sobre travamento no Spanner.
- Saiba mais sobre a capacidade de serialização do Spanner.