Nesta página, descrevemos 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 todo o ciclo de vida 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 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 e a confirmação da transação. Como o Spanner sempre impõe a serializaçã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. Em seguida, o Spanner 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 o Spanner só está progredindo 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 UPDATEem 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_RANGESem instruções DML, mas só é possível usar a cláusulaFOR UPDATEem instruçõesSELECT.
Semântica de bloqueio
Para reduzir as solicitações de gravação simultâneas e o custo das transações canceladas
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 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 não serão 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 a seguir, Transaction 1 bloqueia as células MarketingBudget por
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 de 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 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, 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.
Semântica de consulta
Esta seção fornece orientações sobre a semântica de consulta ao usar a cláusula FOR UPDATE.
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 em 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 ficam 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 executar 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 confirmada.
- Em combinação com a dica
LOCK_SCANNED_RANGES:não é possível usar a cláusulaFOR UPDATEe a dicaLOCK_SCANNED_RANGESna 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 UPDATEem consultas que usam índices de pesquisa de texto completo. - Em transações somente leitura:a cláusula
FOR UPDATEsó é 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 UPDATEem consultas dentro de instruções DDL, que são armazenadas para execução posterior. Por exemplo, não é possível usar a cláusulaFOR UPDATEao definir uma visualização. Se o bloqueio for necessário, a cláusulaFOR UPDATEpoderá ser especificada ao consultar a visualização.
A seguir
- Saiba como usar a cláusula
FOR UPDATEno GoogleSQL e no PostgreSQL. - Saiba mais sobre a dica
LOCK_SCANNED_RANGES. - Saiba mais sobre o bloqueio no Spanner.
- Saiba mais sobre a serialização do Spanner.