Luego de tanto batallar ya dimos con el resultado de esta dificultad que tantos lectores de este sitio han presentado. Si tienes algún detalle que aportar no dejes de dejar tu conocimiento.
- 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
yACCESS EXCLUSIVE
modos de bloqueo.los
SELECT FOR UPDATE
ySELECT FOR SHARE
Los comandos adquieren un bloqueo de este modo en la (s) tabla (s) de destino (además deACCESS SHARE
se bloquea en cualquier otra tabla a la que se hace referencia pero no se seleccionaFOR UPDATE/FOR SHARE
). ROW EXCLUSIVE
-
Conflictos con el
SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
, yACCESS EXCLUSIVE
modos de bloqueo.Los comandos
UPDATE
,DELETE
, yINSERT
adquirir este modo de bloqueo en la tabla de destino (además deACCESS 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
, yACCESS EXCLUSIVE
modos de bloqueo. Este modo protege una tabla contra cambios de esquema concurrentes yVACUUM
carreras.Adquirida por
VACUUM
(sinFULL
),ANALYZE
,CREATE INDEX CONCURRENTLY
,REINDEX CONCURRENTLY
,CREATE STATISTICS
y ciertoALTER INDEX
yALTER 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
, yACCESS EXCLUSIVE
modos de bloqueo. Este modo protege una tabla contra cambios de datos simultáneos.Adquirida por
CREATE INDEX
(sinCONCURRENTLY
). SHARE ROW EXCLUSIVE
-
Conflictos con el
ROW EXCLUSIVE
,SHARE UPDATE EXCLUSIVE
,SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
, yACCESS 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 deALTER TABLE
(ver ALTERAR TABLA). EXCLUSIVE
-
Conflictos con el
ROW SHARE
,ROW EXCLUSIVE
,SHARE UPDATE EXCLUSIVE
,SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
, yACCESS EXCLUSIVE
modos de bloqueo. Este modo permite solo concurrentesACCESS 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
, yACCESS 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
, yREFRESH MATERIALIZED VIEW
(sinCONCURRENTLY
) comandos. Muchas formas deALTER INDEX
yALTER TABLE
Adquirir también un candado en este nivel. Este es también el modo de bloqueo predeterminado paraLOCK TABLE
declaraciones que no especifican un modo explícitamente.
Propina
Solo un
ACCESS EXCLUSIVE
bloquear bloques aSELECT
(sinFOR 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 elSELECT
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 intentanUPDATE
,DELETE
,SELECT FOR UPDATE
,SELECT FOR NO KEY UPDATE
,SELECT FOR SHARE
oSELECT 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 unREPEATABLE READ
oSERIALIZABLE
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 cualquierDELETE
en una fila, y también por unUPDATE
que modifica los valores de determinadas columnas. Actualmente, el conjunto de columnas consideradas para laUPDATE
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 cualquierUPDATE
que no adquiere unFOR 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 transaccionesUPDATE
,DELETE
,SELECT FOR UPDATE
oSELECT FOR NO KEY UPDATE
en estas filas, pero no les impide realizarSELECT FOR SHARE
oSELECT 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 noSELECT FOR NO KEY UPDATE
. Un candado con llave compartida bloquea la ejecución de otras transacciones.DELETE
o cualquierUPDATE
que cambia los valores clave, pero no otrosUPDATE
, y tampoco previeneSELECT FOR NO KEY UPDATE
,SELECT FOR SHARE
, oSELECT 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 |
Tienes la opción de amparar nuestra función añadiendo un comentario y dejando una puntuación te damos las gracias.