Esta página descreve como usar a cláusula FOR UPDATE
no isolamento serializável.
O mecanismo de bloqueio da cláusula FOR UPDATE
é diferente para a
leitura repetível e o
isolamento serializável. Quando usa o isolamento serializável, quando usa a consulta SELECT
para analisar uma tabela, adicionar uma cláusula FOR UPDATE
permite bloqueios exclusivos na interseção do nível de granularidade de linhas e colunas, também conhecido como nível de célula. O bloqueio
permanece em vigor durante a duração da transação de leitura/escrita. Durante este 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 motivo deve usar a cláusula FOR UPDATE
Em bases 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 atualiza os dados entre a leitura dos dados e a confirmação da transação. Uma vez que o Spanner aplica a serialização por predefinição, é garantido que a transação só é confirmada com êxito se os dados acedidos na transação não estiverem desatualizados no momento da confirmação. Por conseguinte, a cláusula FOR UPDATE
não é necessária para garantir a correção das transações no Spanner.
No entanto, em exemplos de utilização com elevada contenção de escrita, como quando várias transações estão a ler e a escrever simultaneamente nos mesmos dados, as transações simultâneas podem causar um aumento nas anulações. Isto acontece porque, quando várias transações simultâneas adquirem bloqueios partilhados e, em seguida, tentam atualizar para bloqueios exclusivos, as transações causam um impasse. O impasse bloqueia permanentemente as transações porque cada uma está a aguardar que a outra liberte o recurso de que precisa. Para progredir, o Spanner anula todas as transações, exceto uma, para resolver o impasse. Para mais informações, consulte o artigo Bloqueio.
Uma transação que usa a cláusula FOR UPDATE
adquire o bloqueio exclusivo de forma proativa e prossegue com a execução, enquanto outras transações aguardam a sua vez para o bloqueio. Embora o Spanner possa continuar a limitar o débito, uma vez que as transações em conflito só podem ser realizadas uma de cada vez, mas como o Spanner só está a progredir numa transação, poupa tempo que, de outra forma, seria gasto a anular e a repetir transações.
Por conseguinte, se for importante reduzir o número de transações anuladas num cenário de pedido de gravação simultâneo, pode usar a cláusula FOR UPDATE
para reduzir o número total de anulações e aumentar a eficiência da execução da carga de trabalho.
Comparação com a sugestão LOCK_SCANNED_RANGES
A cláusula FOR UPDATE
tem uma função semelhante à da sugestão LOCK_SCANNED_RANGES=exclusive
.
Existem duas diferenças principais:
Se usar a sugestão
LOCK_SCANNED_RANGES
, a transação adquire bloqueios exclusivos nos intervalos analisados para toda a declaração. Não pode adquirir bloqueios exclusivos numa subconsulta. A utilização da sugestão de bloqueio pode resultar na aquisição de mais bloqueios do que o necessário e contribuir para a contenção de bloqueios na carga de trabalho. O exemplo seguinte mostra como usar uma sugestão 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, pode usar a cláusula
FOR UPDATE
numa subconsulta, conforme mostrado no seguinte 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;
Pode usar a sugestão
LOCK_SCANNED_RANGES
em declarações DML, enquanto só pode usar a cláusulaFOR UPDATE
em declaraçõesSELECT
.
Semântica de bloqueio
Para reduzir as solicitações de escrita simultâneas e o custo das transações anuladas
devido a um impasse, o Spanner bloqueia os dados ao nível da célula, se
possível. O nível de célula é o nível de dados mais detalhado numa tabela, ou seja, um ponto de dados na interseção de uma linha e uma coluna. Quando usa a cláusula FOR UPDATE
, o Spanner bloqueia células específicas que são analisadas pela consulta SELECT
.
No exemplo seguinte, a célula MarketingBudget
na linha SingerId = 1
e AlbumId = 1
está exclusivamente bloqueada na tabela Albums
, o que impede que as transações simultâneas modifiquem essa célula até que esta transação seja confirmada ou revertida. No entanto, as transações simultâneas podem continuar a atualizar a célula AlbumTitle
nessa linha.
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId = 1
FOR UPDATE;
As transações simultâneas podem bloquear a leitura de dados bloqueados
Quando uma transação adquire bloqueios exclusivos num intervalo analisado, as transações simultâneas podem bloquear a leitura desses dados. O Spanner aplica a serialização para que os dados só possam ser lidos se for garantido que não foram alterados por outra transação durante a duração da transação. As transações simultâneas que tentam ler dados já bloqueados podem ter de aguardar até que a transação que detém os bloqueios seja confirmada, revertida ou exceda o limite de tempo.
No exemplo seguinte, 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á a tentar ler o MarketingBudget
para
AlbumId = 1
, está bloqueado até que Transaction 1
confirme ou seja revertido.
-- Transaction 2
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId = 1;
-- Blocked by Transaction 1
Da mesma forma, uma transação que tente bloquear um intervalo analisado com FOR UPDATE
é bloqueada por uma transação simultânea que bloqueia um intervalo analisado sobreposto.
Transaction 3
no exemplo seguinte também está bloqueado, uma vez que Transaction 1
bloqueou as células MarketingBudget
para 3 <= AlbumId < 5
, que é o intervalo analisado 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
Leia um índice
Uma leitura simultânea pode não ser bloqueada se a consulta que bloqueou o intervalo analisado bloquear as linhas na tabela base, mas a transação simultânea lê a partir de um índice.
O seguinte Transaction 1
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
de leitura não está 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 num intervalo de células com uma sugestão de bloqueio exclusiva, as transações simultâneas que tentam executar uma gravação sem ler primeiro os dados nas células bloqueadas podem prosseguir. A transação é bloqueada no commit até que a transação que detém os bloqueios seja confirmada ou revertida.
O seguinte 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;
Se Transaction 2
tentar atualizar a tabela Albums
, a ação é bloqueada 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 os espaços existentes são bloqueados quando um intervalo analisado é bloqueado
Quando uma transação adquiriu bloqueios exclusivos num intervalo analisado, as transações concorrentes não podem inserir dados nas lacunas desse intervalo.
O seguinte Transaction 1
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, é impedido de o fazer até que Transaction 1
confirme ou reverta.
-- 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
Restrições de aquisição de bloqueios
A semântica de bloqueio descrita fornece orientações gerais, mas não é uma garantia de 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 os bloqueios adquiridos. A cláusula impede que outras transações modifiquem as células bloqueadas até que a transação atual seja concluída.
Sintaxe de consulta
Esta secção fornece orientações sobre a sintaxe de consulta quando usa a cláusula FOR UPDATE
.
A utilização mais comum é numa declaração SELECT
de nível superior. Por exemplo:
SELECT SingerId, SingerInfo
FROM Singers WHERE SingerID = 5
FOR UPDATE;
Este exemplo demonstra como usar a cláusula FOR UPDATE
numa declaração SELECT
para bloquear exclusivamente as células SingerId
e SingerInfo
de WHERE SingerID = 5
.
Use em declarações WITH
A cláusula FOR UPDATE
não adquire bloqueios para a declaração WITH
quando especifica FOR UPDATE
na consulta de nível exterior da declaração WITH
.
Na consulta seguinte, a tabela Singers
não adquire bloqueios porque a intenção de bloquear não é propagada para a consulta de expressões de tabelas 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 CTE, o intervalo analisado da consulta CTE adquire os bloqueios.
No exemplo seguinte, as células SingerId
e SingerInfo
para as linhas
onde SingerId > 5
estão bloqueadas.
WITH s AS
(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5 FOR UPDATE)
SELECT * FROM s;
Use em subconsultas
Pode usar a cláusula FOR UPDATE
numa consulta de nível exterior que tenha uma ou mais subconsultas. Os bloqueios são adquiridos pela consulta de nível superior e nas subconsultas, exceto nas subconsultas de expressões.
A seguinte consulta bloqueia as células SingerId
e SingerInfo
para as linhas onde
SingerId > 5.
(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5) AS t
FOR UPDATE;
A consulta seguinte não bloqueia nenhuma célula na tabela Albums
porque está
numa subconsulta de expressão. As células SingerId
e SingerInfo
das linhas devolvidas pela subconsulta de expressão estão bloqueadas.
SELECT SingerId, SingerInfo
FROM Singers
WHERE SingerId = (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000)
FOR UPDATE;
Use para consultar vistas
Pode usar a cláusula FOR UPDATE
para consultar uma visualização, conforme mostrado no seguinte exemplo:
CREATE VIEW SingerBio AS SELECT SingerId, FullName, SingerInfo FROM Singers;
SELECT * FROM SingerBio WHERE SingerId = 5 FOR UPDATE;
Não pode usar a cláusula FOR UPDATE
quando define uma vista.
Exemplos de utilização não suportados
Os seguintes exemplos de utilização FOR UPDATE
não são suportados:
- Como um mecanismo de exclusão mútua para executar código fora do Spanner: não use o bloqueio no Spanner para garantir o acesso exclusivo a um recurso fora do Spanner. As transações podem ser anuladas pelo Spanner, por exemplo, se uma transação for repetida, quer seja explicitamente pelo código da aplicação ou implicitamente pelo código do cliente, como o controlador JDBC do Spanner, só é garantido que os bloqueios são mantidos durante a tentativa que foi confirmada.
- Em combinação com a sugestão
LOCK_SCANNED_RANGES
: não pode usar a cláusulaFOR UPDATE
e a sugestãoLOCK_SCANNED_RANGES
na mesma consulta. Caso contrário, o Spanner devolve um erro. - Em consultas de pesquisa de texto completo: não pode usar a cláusula
FOR UPDATE
em consultas que usam índices de pesquisa de texto completo. - Em transações só de leitura: a cláusula
FOR UPDATE
só é válida em consultas executadas em transações de leitura/escrita. - Em declarações DDL: não pode usar a cláusula
FOR UPDATE
em consultas em declarações DDL, que são armazenadas para execução posterior. Por exemplo, não pode usar a cláusulaFOR UPDATE
quando define uma vista. Se o bloqueio for necessário, a cláusulaFOR UPDATE
pode ser especificada ao consultar a vista.
O que se segue
- 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 sugestão
LOCK_SCANNED_RANGES
. - Saiba mais sobre o bloqueio no Spanner.
- Saiba mais acerca da serialização do Spanner.