13.3.1. Cerraduras a nivel de mesa
13.3.2. Cerraduras a nivel de fila
13.3.3. Bloqueos a nivel de página
13.3.4. Interbloqueos
13.3.5. Cerraduras de aviso

PostgreSQL proporciona varios modos de bloqueo para controlar el acceso simultáneo a los datos de las tablas. Estos modos se pueden utilizar para el bloqueo controlado por la aplicación en situaciones en las que MVCC no proporciona el comportamiento deseado. Además, la mayoría de los comandos de PostgreSQL adquieren automáticamente bloqueos de los modos apropiados para garantizar que las tablas a las que se hace referencia no se eliminen ni se modifiquen de manera incompatible mientras se ejecuta el comando. (Por ejemplo, TRUNCATE no se puede ejecutar de forma segura al mismo tiempo que otras operaciones en la misma tabla, por lo que obtiene un bloqueo exclusivo en la tabla para hacer cumplir eso).

Para examinar una lista de los bloqueos actualmente pendientes en un servidor de base de datos, utilice el pg_locks vista del sistema. Para obtener más información sobre la supervisión del estado del subsistema del administrador de bloqueo, consulte Capitulo 27.

13.3.1. Cerraduras a nivel de mesa

La siguiente lista muestra los modos de bloqueo disponibles y los contextos en los que PostgreSQL los utiliza automáticamente. También puede adquirir cualquiera de estos bloqueos explícitamente con el comando LOCK. Recuerde que todos estos modos de bloqueo son bloqueos a nivel de tabla, incluso si el nombre contiene la palabra hilera; los nombres de los modos de bloqueo son históricos. Hasta cierto punto, los nombres reflejan el uso típico de cada modo de bloqueo, pero la semántica es la misma. La única diferencia real entre un modo de bloqueo y otro es el conjunto de modos de bloqueo con los que cada uno entra en conflicto (ver Tabla 13.2). Dos transacciones no pueden mantener bloqueos de modos en conflicto en la misma mesa al mismo tiempo. (Sin embargo, una transacción nunca entra en conflicto consigo misma. Por ejemplo, podría adquirir ACCESS EXCLUSIVE bloquear y luego adquirir ACCESS SHARE bloquear en la misma tabla). Los modos de bloqueo no conflictivos se pueden mantener simultáneamente en muchas transacciones. Observe en particular que algunos modos de bloqueo son autoconflictos (por ejemplo, un ACCESS EXCLUSIVE el bloqueo no puede ser retenido por más de una transacción a la vez) mientras que otros no son autoconflictos (por ejemplo, un ACCESS SHARE el bloqueo puede ser retenido por múltiples transacciones).

Modos de bloqueo a nivel de mesa

ACCESS SHARE

Conflictos con el ACCESS EXCLUSIVE modo de bloqueo solamente.

los SELECT El comando adquiere un bloqueo de este modo en las tablas referenciadas. En general, cualquier consulta que solo lee una tabla y no la modifica adquirirá este modo de bloqueo.

ROW SHARE

Conflictos con el EXCLUSIVE y ACCESS EXCLUSIVE modos de bloqueo.

los SELECT FOR UPDATE y SELECT FOR SHARE Los comandos adquieren un bloqueo de este modo en la (s) tabla (s) de destino (además de ACCESS SHARE se bloquea en cualquier otra tabla a la que se hace referencia pero no se selecciona FOR UPDATE/FOR SHARE).

ROW EXCLUSIVE

Conflictos con el SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, y ACCESS EXCLUSIVE modos de bloqueo.

Los comandos UPDATE, DELETE, y INSERT adquirir este modo de bloqueo en la tabla de destino (además de ACCESS SHARE bloqueos en cualquier otra tabla referenciada). En general, este modo de bloqueo será adquirido por cualquier comando que modifica datos en una mesa.

SHARE UPDATE EXCLUSIVE

Conflictos con el SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, y ACCESS EXCLUSIVE modos de bloqueo. Este modo protege una tabla contra cambios de esquema concurrentes y VACUUM carreras.

Adquirida por VACUUM (sin FULL), ANALYZE, CREATE INDEX CONCURRENTLY, REINDEX CONCURRENTLY, CREATE STATISTICSy cierto ALTER INDEX y ALTER TABLE variantes (para obtener detalles completos, consulte ALTER INDEX y ALTER TABLE).

SHARE

Conflictos con el ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE ROW EXCLUSIVE, EXCLUSIVE, y ACCESS EXCLUSIVE modos de bloqueo. Este modo protege una tabla contra cambios de datos simultáneos.

Adquirida por CREATE INDEX (sin CONCURRENTLY).

SHARE ROW EXCLUSIVE

Conflictos con el ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, y ACCESS EXCLUSIVE modos de bloqueo. Este modo protege una tabla contra cambios de datos simultáneos y es autoexclusivo para que solo una sesión pueda mantenerla a la vez.

Adquirida por CREATE TRIGGER y algunas formas de ALTER TABLE (ver ALTERAR TABLA).

EXCLUSIVE

Conflictos con el ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, y ACCESS EXCLUSIVE modos de bloqueo. Este modo permite solo concurrentes ACCESS SHARE bloqueos, es decir, solo las lecturas de la tabla pueden proceder en paralelo con una transacción que mantiene este modo de bloqueo.

Adquirida por REFRESH MATERIALIZED VIEW CONCURRENTLY.

ACCESS EXCLUSIVE

Conflictos con cerraduras de todos los modos (ACCESS SHARE, ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, y ACCESS EXCLUSIVE). Este modo garantiza que el titular es la única transacción que accede a la mesa de alguna manera.

Adquirido por el DROP TABLE, TRUNCATE, REINDEX, CLUSTER, VACUUM FULL, y REFRESH MATERIALIZED VIEW (sin CONCURRENTLY) comandos. Muchas formas de ALTER INDEX y ALTER TABLE Adquirir también un candado en este nivel. Este es también el modo de bloqueo predeterminado para LOCK TABLE declaraciones que no especifican un modo explícitamente.

Propina

Solo un ACCESS EXCLUSIVE bloquear bloques a SELECT (sin FOR UPDATE/SHARE) declaración.

Una vez adquirido, un candado normalmente se mantiene hasta el final de la transacción. Pero si se adquiere un bloqueo después de establecer un punto de guardado, el bloqueo se libera inmediatamente si el punto de guardado se revierte a. Esto es consistente con el principio de que ROLLBACK cancela todos los efectos de los comandos desde el punto de guardado. Lo mismo se aplica a los bloqueos adquiridos dentro de un bloque de excepción PL / pgSQL: un escape de error del bloque libera los bloqueos adquiridos dentro de él.

Cuadro 13.2. Modos de bloqueo en conflicto

Modo de bloqueo solicitado Modo de bloqueo existente
ACCESS SHARE ROW SHARE ROW EXCL. SHARE UPDATE EXCL. SHARE SHARE ROW EXCL. EXCL. ACCESS EXCL.
ACCESS SHARE X
ROW SHARE X X
ROW EXCL. X X X X
SHARE UPDATE EXCL. X X X X X
SHARE X X X X X
SHARE ROW EXCL. X X X X X X
EXCL. X X X X X X X
ACCESS EXCL. X X X X X X X X

13.3.2. Cerraduras a nivel de fila

Además de los bloqueos a nivel de tabla, existen bloqueos a nivel de fila, que se enumeran a continuación con los contextos en los que PostgreSQL los utiliza automáticamente. Consulte la Tabla 13.3 para obtener una tabla completa de conflictos de bloqueo a nivel de fila. Tenga en cuenta que una transacción puede contener bloqueos en conflicto en la misma fila, incluso en diferentes subtransacciones; pero aparte de eso, dos transacciones nunca pueden mantener bloqueos en conflicto en la misma fila. Los bloqueos a nivel de fila no afectan la consulta de datos; ellos bloquean solo escritores y casilleros a la misma fila. Los bloqueos a nivel de fila se liberan al final de la transacción o durante la reversión del punto de guardado, al igual que los bloqueos a nivel de tabla.

Modos de bloqueo a nivel de fila

FOR UPDATE

FOR UPDATE hace que las filas recuperadas por el SELECT declaración para ser bloqueada como si fuera a actualizarse. Esto evita que otras transacciones los bloqueen, modifiquen o eliminen hasta que finalice la transacción actual. Es decir, otras transacciones que intentan UPDATE, DELETE, SELECT FOR UPDATE, SELECT FOR NO KEY UPDATE, SELECT FOR SHARE o SELECT FOR KEY SHARE de estas filas se bloquearán hasta que finalice la transacción actual; en cambio, SELECT FOR UPDATE esperará una transacción concurrente que haya ejecutado cualquiera de esos comandos en la misma fila, y luego bloqueará y devolverá la fila actualizada (o ninguna fila, si la fila fue eliminada). Dentro de un REPEATABLE READ o SERIALIZABLE transacción, sin embargo, se generará un error si una fila a bloquear ha cambiado desde que comenzó la transacción. Para más información, consulte la Sección 13.4.

los FOR UPDATE El modo de bloqueo también lo adquiere cualquier DELETE en una fila, y también por un UPDATE que modifica los valores de determinadas columnas. Actualmente, el conjunto de columnas consideradas para la UPDATE Los casos son aquellos que tienen un índice único que se puede usar en una clave externa (por lo que los índices parciales y los índices de expresión no se consideran), pero esto puede cambiar en el futuro.

FOR NO KEY UPDATE

Se comporta de manera similar a FOR UPDATE, excepto que el bloqueo adquirido es más débil: este bloqueo no bloqueará SELECT FOR KEY SHARE comandos que intentan adquirir un bloqueo en las mismas filas. Este modo de bloqueo también lo adquiere cualquier UPDATE que no adquiere un FOR UPDATE cerrar con llave.

FOR SHARE

Se comporta de manera similar a FOR NO KEY UPDATE, excepto que adquiere un bloqueo compartido en lugar de un bloqueo exclusivo en cada fila recuperada. Un candado compartido bloquea la ejecución de otras transacciones UPDATE, DELETE, SELECT FOR UPDATE o SELECT FOR NO KEY UPDATE en estas filas, pero no les impide realizar SELECT FOR SHARE o SELECT FOR KEY SHARE.

FOR KEY SHARE

Se comporta de manera similar a FOR SHARE, excepto que el bloqueo es más débil: SELECT FOR UPDATE está bloqueado, pero no SELECT FOR NO KEY UPDATE. Un candado con llave compartida bloquea la ejecución de otras transacciones. DELETE o cualquier UPDATE que cambia los valores clave, pero no otros UPDATE, y tampoco previene SELECT FOR NO KEY UPDATE, SELECT FOR SHARE, o SELECT FOR KEY SHARE.

PostgreSQL no recuerda ninguna información sobre las filas modificadas en la memoria, por lo que no hay límite en el número de filas bloqueadas a la vez. Sin embargo, bloquear una fila puede provocar una escritura en el disco, por ejemplo, SELECT FOR UPDATE modifica las filas seleccionadas para marcarlas como bloqueadas, lo que dará como resultado escrituras en disco.

Cuadro 13.3. Bloqueos de nivel de fila en conflicto

Modo de bloqueo solicitado Modo de bloqueo actual
PARA COMPARTIR CLAVE PARA COMPARTIR PARA NO HAY ACTUALIZACIÓN CLAVE PARA ACTUALIZAR
PARA COMPARTIR CLAVE X
PARA COMPARTIR X X
PARA NO HAY ACTUALIZACIÓN CLAVE X X X
PARA ACTUALIZAR X X X X

13.3.3. Bloqueos a nivel de página

Además de los bloqueos de tabla y fila, los bloqueos exclusivos / compartidos a nivel de página se utilizan para controlar el acceso de lectura / escritura a las páginas de la tabla en el grupo de búfer compartido. Estos bloqueos se liberan inmediatamente después de que se recupera o actualiza una fila. Los desarrolladores de aplicaciones normalmente no necesitan preocuparse por los bloqueos a nivel de página, pero se mencionan aquí para que estén completos.

13.3.4. Interbloqueos

El uso de bloqueo explícito puede aumentar la probabilidad de interbloqueos, donde dos (o más) cada una de las transacciones mantiene bloqueos que la otra desea. Por ejemplo, si la transacción 1 adquiere un bloqueo exclusivo en la tabla A y luego intenta adquirir un bloqueo exclusivo en la tabla B, mientras que la transacción 2 ya tiene la tabla B bloqueada de forma exclusiva y ahora quiere un bloqueo exclusivo en la tabla A, entonces ninguno de los dos puede continuar. . PostgreSQL detecta automáticamente situaciones de interbloqueo y las resuelve abortando una de las transacciones involucradas, permitiendo que las otras se completen. (Es difícil predecir exactamente qué transacción se cancelará y no se debe confiar en ella).

Tenga en cuenta que los interbloqueos también pueden ocurrir como resultado de bloqueos a nivel de fila (y, por lo tanto, pueden ocurrir incluso si no se usa el bloqueo explícito). Considere el caso en el que dos transacciones simultáneas modifican una tabla. La primera transacción se ejecuta:

UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 11111;

Esto adquiere un bloqueo de nivel de fila en la fila con el número de cuenta especificado. Luego, la segunda transacción se ejecuta:

UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 22222;
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 11111;

El primero UPDATE declaración adquiere con éxito un bloqueo de nivel de fila en la fila especificada, por lo que tiene éxito en la actualización de esa fila. Sin embargo, el segundo UPDATE La instrucción encuentra que la fila que está intentando actualizar ya se ha bloqueado, por lo que espera a que se complete la transacción que adquirió el bloqueo. La transacción dos ahora está esperando a que se complete la transacción uno antes de continuar con la ejecución. Ahora, la transacción uno se ejecuta:

UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 22222;

La transacción uno intenta adquirir un bloqueo de nivel de fila en la fila especificada, pero no puede: la transacción dos ya tiene dicho bloqueo. De modo que espera a que se complete la transacción dos. Por lo tanto, la transacción uno se bloquea en la transacción dos y la transacción dos se bloquea en la transacción uno: una condición de interbloqueo. PostgreSQL detectará esta situación y abortará una de las transacciones.

La mejor defensa contra los puntos muertos es evitarlos asegurándose de que todas las aplicaciones que utilizan una base de datos adquieran bloqueos en varios objetos en un orden coherente. En el ejemplo anterior, si ambas transacciones hubieran actualizado las filas en el mismo orden, no se habría producido ningún punto muerto. También se debe asegurarse de que el primer bloqueo adquirido en un objeto en una transacción sea el modo más restrictivo que se necesitará para ese objeto. Si no es posible verificar esto con anticipación, los puntos muertos se pueden manejar sobre la marcha volviendo a intentar las transacciones que se anulan debido a los puntos muertos.

Siempre que no se detecte una situación de interbloqueo, una transacción que busque un bloqueo a nivel de tabla o de fila esperará indefinidamente a que se liberen los bloqueos en conflicto. Esto significa que es una mala idea que las aplicaciones mantengan abiertas las transacciones durante largos períodos de tiempo (por ejemplo, mientras esperan la entrada del usuario).

13.3.5. Cerraduras de aviso

PostgreSQL proporciona un medio para crear bloqueos que tienen significados definidos por la aplicación. Estos se llaman cerraduras de aviso, porque el sistema no impone su uso, depende de la aplicación usarlos correctamente. Los bloqueos de aviso pueden resultar útiles para las estrategias de bloqueo que no encajan con el modelo MVCC. Por ejemplo, un uso común de los bloqueos de aviso es emular las estrategias de bloqueo pesimistas típicas de los llamados archivo plano sistemas de gestión de datos. Si bien una bandera almacenada en una tabla podría usarse para el mismo propósito, los bloqueos de aviso son más rápidos, evitan la hinchazón de la tabla y el servidor los limpia automáticamente al final de la sesión.

Hay dos formas de adquirir un bloqueo de aviso en PostgreSQL: a nivel de sesión o a nivel de transacción. Una vez adquirido a nivel de sesión, se mantiene un bloqueo de aviso hasta que se libera explícitamente o la sesión finaliza. A diferencia de las solicitudes de bloqueo estándar, las solicitudes de bloqueo de aviso a nivel de sesión no respetan la semántica de la transacción: un bloqueo adquirido durante una transacción que luego se revierte se mantendrá después de la reversión y, del mismo modo, un desbloqueo es efectivo incluso si la transacción de llamada falla más tarde. Un candado puede adquirirse varias veces mediante su propio proceso; para cada solicitud de bloqueo completa, debe haber una solicitud de desbloqueo correspondiente antes de que se libere realmente el bloqueo. Las solicitudes de bloqueo a nivel de transacción, por otro lado, se comportan más como solicitudes de bloqueo normales: se liberan automáticamente al final de la transacción y no hay una operación de desbloqueo explícita. Este comportamiento suele ser más conveniente que el comportamiento a nivel de sesión para el uso a corto plazo de un bloqueo de aviso. Las solicitudes de bloqueo de nivel de sesión y de transacción para el mismo identificador de bloqueo de aviso se bloquearán entre sí de la forma esperada. Si una sesión ya tiene un bloqueo de aviso dado, las solicitudes adicionales realizadas por ella siempre tendrán éxito, incluso si otras sesiones están esperando el bloqueo; esta afirmación es verdadera independientemente de si la retención de bloqueo existente y la nueva solicitud están a nivel de sesión o de transacción.

Como todos los bloqueos en PostgreSQL, se puede encontrar una lista completa de los bloqueos de aviso que tiene actualmente cualquier sesión en el pg_locks vista del sistema.

Tanto los bloqueos de aviso como los bloqueos regulares se almacenan en un grupo de memoria compartida cuyo tamaño está definido por las variables de configuración max_locks_per_transaction y max_connections. Se debe tener cuidado de no agotar esta memoria o el servidor no podrá otorgar ningún bloqueo. Esto impone un límite superior en la cantidad de bloqueos de aviso que puede otorgar el servidor, generalmente de decenas a cientos de miles, dependiendo de cómo esté configurado el servidor.

En ciertos casos, el uso de métodos de bloqueo de aviso, especialmente en consultas que implican pedidos explícitos y LIMIT cláusulas, se debe tener cuidado de controlar los bloqueos adquiridos debido al orden en el que se evalúan las expresiones SQL. Por ejemplo:

SELECT pg_advisory_lock(id) FROM foo WHERE id = 12345; -- ok
SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100; -- danger!
SELECT pg_advisory_lock(q.id) FROM
(
  SELECT id FROM foo WHERE id > 12345 LIMIT 100
) q; -- ok

En las consultas anteriores, la segunda forma es peligrosa porque el LIMIT no se garantiza que se aplique antes de que se ejecute la función de bloqueo. Esto podría hacer que se adquieran algunos bloqueos que la aplicación no esperaba y, por lo tanto, no se liberarían (hasta que finalice la sesión). Desde el punto de vista de la aplicación, tales bloqueos estarían colgando, aunque todavía se pueden ver en pg_locks.

Las funciones proporcionadas para manipular bloqueos de aviso se describen en la Sección 9.27.10.

Anterior Hasta próximo
13.2. Aislamiento de transacciones Hogar 13.4. Comprobaciones de coherencia de datos a nivel de aplicación