2012-04-09 9 views
8

Estoy frente a un problema de interbloqueo de una función PL/pgSQL en mi base de datos PostgreSQL. Por favor, encontrar la instrucción SQL en el bloque de código (solo ejemplo):Interbloqueo detectado en la función PL/pgSQL

BEGIN 
UPDATE accounts SET balance = 0 WHERE acct_name like 'A%'; 
UPDATE accounts SET balance = balance + 100 WHERE acct_name like '%A'; 
EXCEPTION WHEN OTHERS THEN RAISE NOTICE SQLERRM; 
END; 

He encontrado que el estancamiento se produjo durante esta declaración estaba en marcha. Pero no estoy seguro de que haya otras declaraciones tratando de actualizar esta tabla al mismo tiempo (porque no encontré ninguna en mi sistema de registro).

Entonces, ¿es posible que el interbloqueo haya ocurrido dentro de esta declaración? Por lo que sé, si bloqueamos la declaración completa con BEGIN/END. Habrá la misma transacción y no se debe bloquear por sí mismo.

+0

¿Tiene algún disparador en las cuentas? ¿También usas bloqueo explícito? – strkol

+1

De forma predeterminada, una transacción puede observar cambios cometidos por otras transacciones. Para obtener más información, consulte [Aislamiento de transacciones] (http://www.postgresql.org/docs/current/static/transaction-iso.html) de la documentación de PostgreSQL. –

+0

@strkol Sí, tengo, pero la declaración en ese desencadenador no está relacionada con esta tabla. Para el bloqueo explícito también es sí. –

Respuesta

11

Definitivamente hay algún otro proceso compitiendo por el mismo recurso. Esa es la naturaleza de un punto muerto. Una función como la que muestra nunca puede bloquearse. Ver comment by @kgrittn below, que es un experto en concurrencia en PostgreSQL.

Falta su versión de PostgreSQL. Las versiones modernas plantean un mensaje de error detallado . Ambos procesos que compiten por los recursos se enumeran en detalle con la configuración de registro estándar. Verifica tus registros db.

El hecho de que detecte el error puede evitar que Postgres le brinde todos los detalles. Elimine el bloque EXCEPCIÓN de su función plpgsql, si no obtiene la información en el registro db y vuelva a intentarlo.

Para aliviar interbloqueos, puede hacer una serie de cosas. Si todos sus clientes acceden a los recursos en un orden sincronizado, no pueden producirse interbloqueos. El manual proporciona la estrategia básica para resolver la mayoría de los casos en el capítulo sobre deadlocks.


En cuanto a la versión 8.3 : considerar la actualización a una versión más reciente. En particular, esta mejora en la versión 8.4 debe ser interesante para usted (quoting the release notes):

Al denunciar a un punto muerto, reportar el texto de todas las consultas que participan en el punto muerto en el registro del servidor (Itagaki Takahiro)

Además, la versión 8.3 se encontrará con su end of life in February 2013. Deberías comenzar a considerar la actualización.

Una situación de interbloqueo que implica VACUUM debería haber sido fixed in 8.3.1.

+0

Tengo postgres 8.3.6. Esta declaración solo se usa, por ejemplo. En realidad, utilizo el registro de inserción en otra tabla en lugar de AVISO DE RAIZ. –

+0

Normalmente, el mensaje de error debería darle la información que necesita. Agregué un poco a mi respuesta. –

+0

Muchas gracias por su consejo. Seguiré buscando qué otros procesos estaban tratando de hacer algo con esta tabla. –

-1

En PostgreSQL, begin significa que inicia la transacción por lotes.

Su primera actualización bloqueará las filas para las cuentas WHERE acct_name like 'A%'; Esas filas se bloquean exclusivamente después de la primera actualización.

La segunda actualización intenta abrir exactamente las mismas filas que la primera actualización, para actualizar el error , porque la primera actualización aún NO se ha confirmado.

Por lo tanto, la segunda actualización hit deadlock was rollback.

+0

En PL/pgSQL 'BEGIN' es solo el comienzo de un bloque de código. No es lo mismo que 'BEGIN' /' COMMIT' en SQL simple. –

0

No obtendría un problema de interbloqueo, si agrega commit, para liberar bloqueos exclusivos.

BEGIN 
UPDATE accounts SET balance = 0 WHERE acct_name like 'A%'; 
COMMIT; 
UPDATE accounts SET balance = balance + 100 WHERE acct_name like '%A'; 
EXCEPTION WHEN OTHERS THEN RAISE NOTICE SQLERRM; 
END; 
+1

El procedimiento en plpgsql se ejecuta en el bloque de confirmación de transacción única, por lo que no hay compromiso dentro de los procedimientos. – sharafjaffri

Cuestiones relacionadas