En esta página, se describe cómo usar la cláusula FOR UPDATE
en Spanner.
Cuando uses la consulta SELECT
para analizar una tabla, agrega una cláusula FOR UPDATE
para habilitar bloqueos exclusivos a nivel de la granularidad de filas y columnas, también conocido como nivel de celda. El bloqueo permanece en su lugar durante el ciclo de vida de la transacción de lectura y escritura. Durante este tiempo, la cláusula FOR UPDATE
evita que otras transacciones modifiquen las celdas bloqueadas hasta que se complete la transacción actual. Para obtener más información, consulta las guías de referencia de FOR UPDATE
de GoogleSQL y PostgreSQL.
Por qué usar la cláusula FOR UPDATE
En bases de datos con niveles de aislamiento menos estrictos, es posible que la cláusula FOR UPDATE
sea necesaria para garantizar que una transacción simultánea no actualice los datos entre la lectura de los datos y la confirmación de la transacción. Dado que Spanner siempre aplica la serialización, se garantiza que la transacción solo se confirme correctamente si los datos a los que se accede dentro de la transacción no están inactivos en el momento de la confirmación. Por lo tanto, no es necesario usar la cláusula FOR UPDATE
para garantizar la exactitud de las transacciones en Spanner.
Sin embargo, en casos de uso con una disputa de escritura alta, como cuando varias transacciones leen y escriben simultáneamente en los mismos datos, las transacciones simultáneas pueden causar un aumento en las anulaciones. Esto se debe a que, cuando varias transacciones simultáneas adquieren bloqueos compartidos y, luego, intentan actualizarse a bloqueos exclusivos, las transacciones provocan un interbloqueo. Luego, Spanner anula todas las transacciones, excepto una. Para obtener más información, consulta Bloqueo.
Una transacción que usa la cláusula FOR UPDATE
adquiere el bloqueo exclusivo y procede a ejecutarse, mientras que otras transacciones esperan su turno para el bloqueo.
Aunque Spanner aún podría limitar la capacidad de procesamiento porque las transacciones en conflicto solo se pueden realizar de a una, como Spanner solo avanza en una transacción, ahorra tiempo que, de lo contrario, se usaría en anular y reintentar transacciones.
Por lo tanto, si es importante reducir la cantidad de transacciones abortadas en una situación de solicitud de escritura simultánea, puedes usar la cláusula FOR UPDATE
para reducir la cantidad total de abortos y aumentar la eficiencia de la ejecución de la carga de trabajo.
Comparación con la sugerencia LOCK_SCANNED_RANGES
La cláusula FOR UPDATE
tiene una función similar a la sugerencia LOCK_SCANNED_RANGES=exclusive
.
Existen dos diferencias clave:
Si usas la sugerencia
LOCK_SCANNED_RANGES
, la transacción adquiere bloqueos exclusivos en los rangos analizados para toda la sentencia. No puedes adquirir bloqueos exclusivos en una subconsulta. El uso de la sugerencia de bloqueo puede provocar la adquisición de más bloqueos de lo necesario y contribuir a la contención de bloqueos en la carga de trabajo. En el siguiente ejemplo, se muestra cómo usar una sugerencia de bloqueo:@{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 otro lado, puedes usar la cláusula
FOR UPDATE
en una subconsulta, como se muestra en el siguiente ejemplo: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;
Puedes usar la sugerencia
LOCK_SCANNED_RANGES
en las sentencias DML, mientras que solo puedes usar la cláusulaFOR UPDATE
en las sentenciasSELECT
.
Semántica de bloqueo
Para reducir las solicitudes de escritura simultáneas y el costo de las transacciones que se abortan
como resultado de un interbloqueo, Spanner bloquea los datos a nivel de la celda si
es posible. Cuando usas la cláusula FOR UPDATE
, Spanner bloquea
las celdas específicas que analiza la consulta SELECT
.
En el siguiente ejemplo, la celda MarketingBudget
en las filas SingerId = 1
y AlbumId = 1
está bloqueada de forma exclusiva en la tabla Albums
, lo que evita que las transacciones simultáneas modifiquen esa celda hasta que se confirme o revierta esta transacción. Sin embargo, las transacciones simultáneas aún pueden actualizar la celda AlbumTitle
en esa fila.
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId = 1
FOR UPDATE;
Es posible que las transacciones simultáneas bloqueen la lectura de datos bloqueados.
Cuando una transacción adquiere bloqueos exclusivos en un rango analizado, las transacciones simultáneas pueden bloquear la lectura de esos datos. Spanner aplica la serialización para que los datos solo se puedan leer si se garantiza que otra transacción no los modificará durante su ciclo de vida. Es posible que las transacciones simultáneas que intentan leer datos ya bloqueados deban esperar hasta que se confirme o revierta la transacción que mantiene los bloqueos.
En el siguiente ejemplo, Transaction 1
bloquea las celdas 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 intenta leer el MarketingBudget
para AlbumId = 1
, está bloqueado hasta que Transaction 1
se confirme o se revierta.
-- Transaction 2
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId = 1;
-- Blocked by Transaction 1
Del mismo modo, una transacción que intenta bloquear un rango analizado con FOR UPDATE
está bloqueada por una transacción simultánea que bloquea un rango analizado superpuesto.
Transaction 3
en el siguiente ejemplo también está bloqueado, ya que Transaction 1
bloqueó las celdas MarketingBudget
para 3 <= AlbumId < 5
, que es el rango de análisis superpuesto con Transaction 3
.
-- Transaction 3
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 3 and AlbumId < 10
FOR UPDATE;
-- Blocked by Transaction 1
Cómo leer un índice
Es posible que no se bloquee una operación de lectura simultánea si la consulta que bloqueó el rango analizado bloquea las filas de la tabla base, pero la transacción simultánea lee desde un índice.
El siguiente Transaction 1
bloquea las celdas SingerId
y SingerInfo
para SingerId = 1
.
-- Transaction 1
SELECT SingerId, SingerInfo
FROM Singers
WHERE SingerId = 1
FOR UPDATE;
Los bloqueos adquiridos en Transaction 1
no bloquean el Transaction 2
de solo lectura, ya que consulta una tabla de índices.
-- Transaction 2
SELECT SingerId FROM Singers;
Las transacciones simultáneas no bloquean las operaciones DML en datos ya bloqueados.
Cuando una transacción adquiere bloqueos en un rango de celdas con una sugerencia de bloqueo exclusivo, las transacciones simultáneas que intentan realizar una operación de escritura sin leer primero los datos en las celdas bloqueadas pueden continuar. La transacción bloquea la confirmación hasta que la transacción que contiene los bloqueos se confirme o revierta.
El siguiente Transaction 1
bloquea las celdas MarketingBudget
para 1 <= AlbumId < 5
.
-- Transaction 1
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 5
FOR UPDATE;
Si Transaction 2
intenta actualizar la tabla Albums
, se le bloquea esta acción hasta que Transaction 1
confirme o revierta la operación.
-- Transaction 2
UPDATE Albums
SET MarketingBudget = 200000
WHERE SingerId = 1 and AlbumId = 1;
> Query OK, 1 rows affected
COMMIT;
-- Blocked by Transaction 1
Las filas y los espacios existentes se bloquean cuando se bloquea un rango escaneado.
Cuando una transacción adquiere bloqueos exclusivos en un rango analizado, las transacciones simultáneas no pueden insertar datos en los espacios dentro de ese rango.
El siguiente Transaction 1
bloquea las celdas MarketingBudget
para 1 <= AlbumId < 10
.
-- Transaction 1
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 10
FOR UPDATE;
Si Transaction 2
intenta insertar una fila para AlbumId = 9
que aún no existe, se le impide hacerlo hasta que Transaction 1
confirme o revierta la operación.
-- 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
Consideraciones sobre la adquisición de bloqueos
La semántica de bloqueos descrita proporciona orientación general, pero no es una garantía de cómo se pueden adquirir los bloqueos cuando Spanner ejecuta una transacción que usa la cláusula FOR UPDATE
. Los mecanismos de optimización de consultas de Spanner también pueden afectar qué bloqueos se adquieren. La cláusula impide que otras transacciones modifiquen las celdas bloqueadas hasta que se complete la transacción actual.
Semántica de las consultas
En esta sección, se proporciona orientación sobre la semántica de las consultas cuando se usa la cláusula FOR UPDATE
.
Uso en sentencias WITH
La cláusula FOR UPDATE
no adquiere bloqueos para la sentencia WITH
cuando especificas FOR UPDATE
en la consulta de nivel externo de la sentencia WITH
.
En la siguiente consulta, la tabla Singers
no adquiere bloqueos, ya que la intención de bloqueo no se propaga a la consulta de expresiones de tabla comunes (CTE).
WITH s AS (SELECT SingerId, SingerInfo FROM Singers WHERE SingerID > 5)
SELECT * FROM s
FOR UPDATE;
Si se especifica la cláusula FOR UPDATE
en la consulta de CTE, el rango analizado de la consulta de CTE adquiere los bloqueos.
En el siguiente ejemplo, las celdas SingerId
y SingerInfo
de las filas en las que SingerId > 5
están bloqueadas.
WITH s AS
(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5 FOR UPDATE)
SELECT * FROM s;
Uso en subconsultas
Puedes usar la cláusula FOR UPDATE
en una consulta de nivel externo que tenga una o más subconsultas. La consulta de nivel superior y las subconsultas adquieren bloqueos, excepto en las subconsultas de expresión.
La siguiente consulta bloquea las celdas SingerId
y SingerInfo
para las filas en las que SingerId > 5.
(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5) AS t
FOR UPDATE;
La siguiente consulta no bloquea ninguna celda en la tabla Albums
porque se encuentra dentro de una subconsulta de expresión. Las celdas SingerId
y SingerInfo
de las filas que muestra la subconsulta de expresión están bloqueadas.
SELECT SingerId, SingerInfo
FROM Singers
WHERE SingerId = (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000)
FOR UPDATE;
Se usa para consultar vistas
Puedes usar la cláusula FOR UPDATE
para consultar una vista, como se muestra en el siguiente ejemplo:
CREATE VIEW SingerBio AS SELECT SingerId, FullName, SingerInfo FROM Singers;
SELECT * FROM SingerBio WHERE SingerId = 5 FOR UPDATE;
No puedes usar la cláusula FOR UPDATE
cuando defines una vista.
Casos de uso no admitidos
Los siguientes casos de uso de FOR UPDATE
no son compatibles:
- Como mecanismo de exclusión mutua para ejecutar código fuera de Spanner: No uses bloqueos en Spanner para garantizar el acceso exclusivo a un recurso fuera de Spanner. Spanner puede anular las transacciones, por ejemplo, si se vuelve a intentar una transacción, ya sea de forma explícita por el código de la aplicación o de forma implícita por el código del cliente, como el controlador JDBC de Spanner. Solo se garantiza que las cerraduras se mantengan durante el intento que se confirmó.
- En combinación con la sugerencia
LOCK_SCANNED_RANGES
: No puedes usar la cláusulaFOR UPDATE
ni la sugerenciaLOCK_SCANNED_RANGES
en la misma consulta, o Spanner mostrará un error. - En las consultas de búsqueda en el texto completo: No puedes usar la cláusula
FOR UPDATE
en las consultas que usan índices de búsqueda en el texto completo. - En transacciones de solo lectura: La cláusula
FOR UPDATE
solo es válida en las consultas que se ejecutan dentro de transacciones de lectura y escritura. - Dentro de las declaraciones DDL: No puedes usar la cláusula
FOR UPDATE
en consultas dentro de las declaraciones DDL, que se almacenan para su ejecución posterior. Por ejemplo, no puedes usar la cláusulaFOR UPDATE
cuando defines una vista. Si se requiere el bloqueo, se puede especificar la cláusulaFOR UPDATE
cuando se consulta la vista.
Pasos siguientes
- Aprende a usar la cláusula
FOR UPDATE
en GoogleSQL y PostgreSQL. - Obtén más información sobre la sugerencia
LOCK_SCANNED_RANGES
. - Obtén información sobre el bloqueo en Spanner.
- Obtén información sobre la serialización de Spanner.