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.
- Crear tabla A, B, C.
- solicitudes de escritura próximos al servidor para actualizar la Tabla D, en A o B o C.
- 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?
¿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? –
@Darhazer pregunta actualizada con detalles de las transacciones. Su ayuda es muy apreciada. – Aakash
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 –