Trabajando alrededor de error de MySQL “Punto muerto encontró cuando se trata de obtener el bloqueo; intente reiniciar la transacción”

StackOverflow https://stackoverflow.com/questions/2596005

Pregunta

Tengo una tabla de MySQL con cerca de 5.000.000 filas que se están actualizando constantemente en pequeñas maneras por procesos paralelos Perl se conectan a través de DBI. La tabla tiene sobre 10 columnas y varios índices.

Una operación bastante común da lugar al error de seguimiento veces:

DBD::mysql::st execute failed: Deadlock found when trying to get lock; try restarting transaction at Db.pm line 276.

La sentencia SQL que desencadena el error es algo como esto:

UPDATE file_table SET a_lock = 'process-1234' WHERE param1 = 'X' AND param2 = 'Y' AND param3 = 'Z' LIMIT 47

El error se dispara sólo a veces. Yo estimaría en el 1% de las llamadas o menos. Sin embargo, nunca sucedió con una pequeña mesa y se ha vuelto más común como la base de datos ha crecido.

Tenga en cuenta que estoy usando el campo a_lock en file_table para asegurar que los cuatro procesos casi idénticos estoy corriendo no tratar de trabajar en la misma fila. El límite está diseñado para romper su trabajo en pequeños trozos.

No he hecho mucho de sintonía en MySQL o DBD :: mysql. MySQL es una implementación estándar de Solaris, y la conexión de base de datos está configurado de la siguiente manera:

my $dsn = "DBI:mysql:database=" . $DbConfig::database . ";host=${DbConfig::hostname};port=${DbConfig::port}";
my $dbh = DBI->connect($dsn, $DbConfig::username, $DbConfig::password, { RaiseError => 1, AutoCommit => 1 }) or die $DBI::errstr;

he visto en línea que varias otras personas han informado de errores similares y que esto puede ser una situación de bloqueo genuino.

Tengo dos preguntas:

  1. ¿Qué es exactamente mi situación está causando el error anterior?

  2. ¿Hay una manera fácil de trabajar alrededor de ella o disminuir su frecuencia? Por ejemplo, ¿cómo es exactamente hago para "reiniciar transacción en línea Db.pm 276"?

Gracias de antemano.

¿Fue útil?

Solución

Si está utilizando InnoDB o cualquier RDBMS transaccional a nivel de fila, entonces es posible que cualquier transacción de escritura puede provocar un estancamiento, incluso en situaciones perfectamente normales. tablas más grandes, escribe más grandes, y los bloques de transacción largos suelen aumentar la probabilidad de bloqueos que se producen. En su situación, es probable que sea una combinación de éstos.

La única manera de manejar los puntos muertos de verdad es escribir el código para esperar. Esto generalmente no es muy difícil si el código de la base de datos está bien escrito. A menudo sólo se puede poner un try/catch torno a la lógica de ejecución de consultas y buscar un punto muerto cuando se producen errores. Si descubre a uno, lo normal que hacer es tratar de ejecutar la consulta vuelto a fallar.

le recomiendo que lea esta página en el manual de MySQL. Tiene una lista de cosas por hacer para ayudar a hacer frente a los puntos muertos y reducir su frecuencia.

Otros consejos

La respuesta es correcta, sin embargo, la documentación de Perl sobre cómo manejar los puntos muertos es un poco escasa y quizá confundiendo con opciones PrintError, raiseError y handleError. Parece que en lugar de ir con HandleError, su uso en impresión y elevar y luego usar algo como Probar: Tiny para envolver su código y comprobar si hay errores. El siguiente código muestra un ejemplo donde el código db está dentro de un bucle while que volver a ejecutar una instrucción SQL con errores cada 3 segundos. El bloque catch recibe $ _ que es el mensaje ERR específica. Paso esta a una función de controlador "dbi_err_handler", que comprueba $ _ contra una gran cantidad de errores y devuelve 1 si el código debe continuar (rompiendo así el bucle) o 0 si es un callejón sin salida y debe ser juzgado de ...

$sth = $dbh->prepare($strsql);
my $db_res=0;
while($db_res==0)
{
   $db_res=1;
   try{$sth->execute($param1,$param2);}
   catch
   {
       print "caught $_ in insertion to hd_item_upc for upc $upc\n";
       $db_res=dbi_err_handler($_); 
       if($db_res==0){sleep 3;}
   }
}

dbi_err_handler debe tener al menos la siguiente:

sub dbi_err_handler
{
    my($message) = @_;
    if($message=~ m/DBD::mysql::st execute failed: Deadlock found when trying to get lock; try restarting transaction/)
    {
       $caught=1;
       $retval=0; # we'll check this value and sleep/re-execute if necessary
    }
    return $retval;
}

Debe incluir otros errores que desea manejar y conjunto $ retval dependiendo de si desea volver a ejecutar o continuar ..

Espero que esto ayude a alguien -

Tenga en cuenta que si utiliza SELECT FOR UPDATE para realizar un control de la unicidad antes de una inserción, obtendrá un callejón sin salida para cada condición de carrera a menos que habilite la opción innodb_locks_unsafe_for_binlog. Un método libre de interbloqueo para comprobar singularidad es a ciegas insertar una fila en una tabla con un índice único usando INSERT IGNORE, a continuación, para comprobar el número de filas afectado.

añadir debajo de la línea de archivo my.cnf

innodb_locks_unsafe_for_binlog = 1

#

1 - EN
0 - OFF

#

La idea de volver a intentar la consulta en caso de bloqueo excepción es buena, pero puede ser terriblemente lento, ya que la consulta mysql mantendrá a la espera de bloqueos para ser liberados. Y en caso de estancamiento de MySQL está tratando de encontrar si hay algún punto muerto, e incluso después de descubrir que hay un estancamiento, se espera un tiempo antes de expulsar a un hilo con el fin de salir de la situación de bloqueo.

Lo que hice cuando me enfrenté a esta situación es poner en práctica el bloqueo en su propio código, ya que es el mecanismo de bloqueo de MySQL está fallando debido a un error. Así que he implementado mi propia bloqueo a nivel de fila en mi código java:

private HashMap<String, Object> rowIdToRowLockMap = new HashMap<String, Object>();
private final Object hashmapLock = new Object();
public void handleShortCode(Integer rowId)
{
    Object lock = null;
    synchronized(hashmapLock)
    {
      lock = rowIdToRowLockMap.get(rowId);
      if (lock == null)
      {
          rowIdToRowLockMap.put(rowId, lock = new Object());
      }
    }
    synchronized (lock)
    {
        // Execute your queries on row by row id
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top