2011-08-21 11 views
5

Digamos que recibo 1000 solicitudes en nuestros servidores para actualizar una sola tabla MySQL. Los problemas de interbloqueo ocurren inevitablemente en esta situación. Hemos vuelto a intentar la publicación de la transacción como se recomienda para los interbloqueos, pero aún ocurren.¿El motor MySQL InnoDB queue DB DB Triggers automáticamente?

Estamos pensando en encontrar una solución alternativa a continuación.

  1. Crear tabla A, B, C.
  2. solicitudes de escritura próximos al servidor para actualizar la Tabla D, en A o B o C.
  3. Crear un desencadenador de inserción en las Tablas A, B y C respectivamente eso a su vez escribirá los datos en la Tabla D en lugar de exponer directamente la Tabla D a las 1000 solicitudes que llegan al servidor.

Así que nuestra pregunta es cuando esto ocurre y varias filas se escribe en la tabla A, B y C los disparadores subyacentes en las tablas A, B y C podrían disparar al mismo tiempo para actualizar la Tabla D.

¿El motor MySQL InnoDB hace cola automáticamente estos desencadenantes o deberíamos tener que manejar esto en nuestro código?

Cualquier ayuda es muy apreciada.

La tabla D que se está actualizando directamente por todas estas solicitudes ahora y donde se produce el interbloqueo tiene este aspecto.

v_user_email varchar(60) NO PRI  
v_device_IMEI varchar(40) NO PRI  
i_adid   int(11)   NO PRI  
i_impressions int(4)   YES 0 
dt_pulllogdttm datetime NO   
c_created_by char(15) NO   
dt_created_on datetime NO   
c_modified_by char(15) YES   
dt_modified_on datetime YES 

PHP que inserta/actualiza filas en esta tabla se ve así a continuación. Verá que tratamos de publicar la transacción 3 veces si falla debido a un punto muerto, pero hay transacciones que fallan incluso en ese momento y el registro dice que debido a un punto muerto.

$updateQuery = "UPDATE tb_ad_pull_log SET i_impressions = (i_impressions + 1), dt_pulllogdttm = SYSDATE(), c_modified_by = '$createdBy', dt_modified_on = SYSDATE() WHERE v_user_email = '$email' AND i_adid = $adId"; 
     if(ExecuteDeadLockQuery($updateQuery, "UPDATE", __LINE__) == 0) // If there is no record for this ad for the user, insert a new record 
     { 
      $insertQuery = "INSERT INTO tb_ad_pull_log VALUES('$email', '$device_IMEI', $adId, 1, SYSDATE(), '$createdBy', SYSDATE(), NULL, NULL)"; 
      ExecuteDeadLockQuery($insertQuery, "INSERT", __LINE__); 
     }  

función ExecuteDeadLockQuery se parece a esto -

function ExecuteDeadLockQuery($query, $activity, $lineNumber) 
    { 
     global $errorLoggingPath; 
     $maxAttempts = 3; 
     $currentTry = 1; 
     $noOfAffectedRows = -1; 

     while($currentTry <= $maxAttempts) 
     { 
      $currentTry++; 

      mysql_query($query); 

      if(mysql_errno() <> 0) // If error occured 
      { 
       continue; 
      } 
      else 
      { 
       $noOfAffectedRows = mysql_affected_rows(); 
       break; 
      }   
     } 

     if($noOfAffectedRows == -1) // Query never executed successfully 
     { 
      LogError($activity . " failed in tb_ad_pull_log: " . mysql_error(), __FILE__, $lineNumber , $errorLoggingPath); 
     } 

     return $noOfAffectedRows; 
    } 

¿Hay una manera más limpia para evitar este estancamiento? Aquí hay algunos registros que tenemos.

ERROR: 08-21-2011 14:09:57 UPDATE failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 83 
ERROR: 08-21-2011 14:09:57 INSERT failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 86 
ERROR: 08-21-2011 14:09:57 INSERT failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 86 
ERROR: 08-21-2011 14:09:57 UPDATE failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 83 
ERROR: 08-21-2011 14:09:57 INSERT failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 86 
ERROR: 08-21-2011 14:09:57 UPDATE failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 83 
ERROR: 08-21-2011 14:09:59 UPDATE failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 83 
ERROR: 08-21-2011 14:09:59 UPDATE failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 83 
ERROR: 08-21-2011 14:10:01 UPDATE failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 83 
ERROR: 08-21-2011 14:10:01 INSERT failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 86 

La línea 83 es ​​la instrucción UPDATE en PHP y 86 es el INSERT. Tenga en cuenta que estos datos se pueden escribir en esta tabla a razón de 5-8 transacciones por segundo.

Otros detalles

Con cada inserción y actualización en la tabla D ejecuta un disparador que las actualizaciones de tabla X e Y. Es esta una razón para la Tabla D permanezca cerrada y por lo tanto las peticiones de entrada para crear una ¿punto muerto?

Finalmente recibí el problema, pero no estoy seguro de cómo resolverlo. Los activadores DESPUÉS DE INSERTAR y DESPUÉS ACTUALIZAR en la TABLA D bloquean la tabla cuando se activan y, por lo tanto, el interbloqueo de las solicitudes entrantes. Por qué estoy tan seguro de esto es porque una vez que descarté estos desencadenantes, el registro dejó de registrar mensajes de interbloqueo registrados de lo contrario

Fragmento del código de desencadenante.

CREATE DEFINER=CURRENT_USER TRIGGER tuadmin.t_update_CPM_updateBalance 
AFTER UPDATE 
ON tb_ad_pull_log 
FOR EACH ROW 
BEGIN 

    DECLARE `cpm_value` decimal(10,4); 
    DECLARE `clientid` int(4); 

    /* Execute the below block if the requested ad is not the default ad */ 
    IF NEW.i_adid <> 1 THEN 

     SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; 
      //do updates to TABLE X and Y logic 
END 

Aquí es donde yo no entiendo ¿por qué estos factores desencadenantes mantener un bloqueo en la Tabla D y no dejar que cualquier inserción/actualización ocurra simultáneamente.

¿Esto evitará todos los problemas si soltamos los factores desencadenantes y simplemente llamamos un SP del PHP para hacer el trabajo?

+0

¿cómo se ven sus transacciones? Para tener un punto muerto, debe tener más de 1 transacción, que ya actualizó una fila e intenta actualizar la segunda fila, pero esa fila fue actualizada por otra transacción. Las inserciones Simle, así como las actualizaciones solo en 1 fila, no causarán un interbloqueo. ¿Quizás necesites ordenar/limitar tus consultas de actualización? –

+0

@Darhazer pregunta actualizada con detalles de las transacciones. Su ayuda es muy apreciada. – Aakash

+0

tiene índice en v_user_email/i_adid. Si no, eso está causando un punto muerto, porque la consulta de actualización debe analizar todos los registros –

Respuesta

0

En este caso, lo que los DBA MYSQL suelen tener es una característica llamada "replicación", que divide un solo servidor en muchos servidores según sea necesario para equilibrar las cargas. Puede hacerlo utilizando un solo hardware potente, dividido en 2 o más servidores virtuales que se ejecutan dentro de dispositivos virtuales con VirtualBox, VirtualPC o su sabor de virtualización, con la función de replicación MYSQL habilitada.

Puede sintonizar un solo servidor para escrituras (sus actualizaciones en este caso) y otros servidores para consultas que leen datos. Consulte la documentación de replicación de MYSQL here

1

¿Está bien, entonces está utilizando una sola tabla y algunos factores desencadenantes?

¿Y tiene muy pocas transacciones por segundo?

¿Y tiene problemas de bloqueo extraños?

uso de PostgreSQL, estoy bastante seguro de lo siguiente: a) No tendrá esos problemas b) Si las tiene, tendrá el apoyo de la comunidad en ningún momento

Hay un 99,99% de posibilidades de que tu problema sea causado por VERY_SLOW_TRIGGERS, me refiero a totalmente enormemente muy lento, porque solo 8 por segundo implican un tiempo de transacción de 125ms que es ... enorme.

El motivo del bloqueo es evidente, está llamando a un desencadenador en la tabla D.

-> call modification on table D 
-> before mod trigger 
-> modification 
-> after mod trigger 
-> modification complete 

POR EJEMPLO todo lo que sucede en su desencadenador es parte de la transacción en la tabla D y, por lo tanto, mantendrá el bloqueo hasta que finalice.

Puede:

a) cerradura menos filas

b) Bloqueo menos tiempo -> insertar en otra mesa, asíncrono proceso desde allí

c) utilizar un RDBMS que soporta activa correctamente

La opción de equilibrio es la opción de martillo contra mosca, no hay ninguna razón por la que necesite más de un servidor para un conteo de tps tan bajo.

Sin embargo, debe solucionar el funcionamiento de su desencadenante y verificar que no se encuentre con congestión de E/S en algún lugar (generalmente lo que es innecesariamente lento también suele usar demasiados recursos valiosos).

Muy bien, aquí hay otra opción:

DESBLOQUEO TABLAS libera explícitamente cualquier bloqueos de tabla en poder de la sesión actual.

SI su última acción es que la actualización/insertar y si su falla de disparo es imposible o no es un problema

entonces se podría usar esto al comienzo de su disparador, la liberación de todos los bloqueos y pidiendo sólo para el lectura consistente sin bloqueo

1

actualizar e insertar en mysql bloqueado y sincronizado opration, suponga que tiene 2 solicitud proviene de 2 disparador para actualizar la tabla D, cuando 1 está actualizando la tabla D segundo es esperado en la cola.para seleccionar no tiene un bloque sincronizado 2 thread puede solicitar al mismo tiempo. si desea hacer esto posible transacción en el mismo tiempo, debe compilar la replicación

Cuestiones relacionadas