2011-08-21 9 views
5

Diciamo che ho ricevuto 1000 richieste sui nostri server per aggiornare una singola tabella MySQL. I problemi di deadlock si verificano inevitabilmente in questa situazione. Abbiamo tentato nuovamente di postare la transazione come consigliato per deadlock ma si verificano ancora.Il motore MySQL InnoDB accoda automaticamente i trigger DB?

Stiamo pensando di trovare una soluzione alternativa di seguito.

  1. Crea tabella A, B, C.
  2. richieste di scrittura vengono al server per aggiornare la tabella D, in A o B o C.
  3. Creare un trigger INSERT in tabelle A, B e C, rispettivamente, che a sua volta scriverà i dati nella tabella D invece di esporre direttamente la tabella D alle 1000 richieste che giungono al server.

Quindi la nostra domanda è quando questo accade e più righe viene scritto nella tabella A, B e C i trigger sottostanti su tabelle A, B e C possono sparare contemporaneamente aggiornare Tabella D.

Il motore InnoDB di MySQL mette automaticamente in coda questi trigger o dovremmo gestirlo nel nostro codice?

Qualsiasi aiuto è molto apprezzato.

La tabella D che viene aggiornata direttamente da tutte queste richieste ora e dove si verifica il deadlock è simile a questa.

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 che inserisce/aggiorna le righe in questa tabella è come segue. Vedrai che proviamo a postare la transazione 3 volte se fallisce a causa di un deadlock ma ci sono transazioni che falliscono anche in quel momento e il log dice a causa di deadlock.

$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__); 
     }  

funzione ExecuteDeadLockQuery assomiglia a questo -

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; 
    } 

C'è un modo più pulito per evitare questo stallo? Ecco alcuni registri che abbiamo.

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 riga 83 è l'istruzione UPDATE nel PHP e 86 è l'INSERT. Si prega di tenere presente che questi dati possono essere scritti in questa tabella al ritmo di 5-8 transazioni al secondo.

Ulteriori informazioni

Con ogni inserimento e aggiornamento nella tabella D un trigger esegue gli aggiornamenti che TAVOLO X e Y. TAVOLA E 'questo un motivo di tabella D di rimanere bloccata e quindi le richieste in arrivo ottiene un deadlock?

Alla fine ho avuto il problema ma non sono sicuro di come risolverlo. I trigger AFTER INSERT e AFTER UPDATE su TABLE D bloccano la tabella quando vengono attivati ​​e quindi il deadlock delle richieste in entrata. Perché sono così sicuro che questo è perché una volta che ho lasciato cadere questi trigger il registro ha interrotto la registrazione dei messaggi deadlock registrati altrimenti

Snippet del codice di trigger.

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 

Questo è dove non capisco perché dovrebbero questi trigger tenere un blocco su Tabella D e non lasciare alcun inserimento/aggiornamento accadere contemporaneamente.

Questo eviterà tutti i problemi se rilasciamo i trigger e richiamiamo semplicemente un SP dal PHP per eseguire il lavoro?

+0

come sono le transazioni? Per avere un deadlock, dovresti avere più di una transazione, che ha già aggiornato una riga e prova ad aggiornare la seconda riga, ma quella riga è stata aggiornata da un'altra transazione. Gli inserti di Simle, così come gli aggiornamenti solo su 1 riga, non causano deadlock. Forse hai bisogno di ordinare per/limitare le tue domande di aggiornamento? –

+0

@Darhazer ha aggiornato la domanda con i dettagli delle transazioni. Il tuo aiuto è molto apprezzato. – Aakash

+0

hai indice su v_user_email/i_adid. In caso contrario, ciò provoca un deadlock, perché la query di aggiornamento deve eseguire la scansione di tutti i record –

risposta

0

In questo caso ciò che MySQL DBA utilizzare avere è una funzione denominata "replica", cioè dividere un singolo server in molti server sono necessarie per il bilanciamento dei carichi. Puoi farlo utilizzando un singolo hardware potente, diviso in 2 o più server virtuali che funzionano all'interno di appliance virtuali con VirtualBox, VirtualPC o il tuo aspetto di virtualizzazione, con la funzione di replica di MYSQL abilitata.

È possibile ottimizzare un singolo server per le scritture (gli aggiornamenti in questo caso) e altri server per le query che leggono i dati. Vedi documentazione di replica di MYSQL here

1

Ok, quindi stai usando una tabella singola e alcuni trigger?

E hai solo pochissime transazioni al secondo?

E hai problemi di blocco strani?

Usa PostgreSQL, io sono abbastanza sicuro di quanto segue: a) Essa non avrà questi problemi b) Se li ha, avrete il sostegno della comunità in nessun tempo

C'è 99,99% di probabilità che il tuo problema sia causato da VERY_SLOW_TRIGGERS, intendo come estremamente enormemente lento, perché solo 8 al secondo implica un tempo di esecuzione della transazione di 125 ms che è .. enorme.

Il motivo del blocco è evidente, si chiama un trigger nella tabella D.

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

OSSIA tutto ciò che accade nel trigger è parte della transazione sulla tabella D e manterrà quindi il blocco finché non sarà terminato.

È possibile:

una) Blocco meno righe

b) bloccare meno tempo -> inserire in un altro tavolo, asincrona processo da lì

c) utilizzare un RDBMS che supporta trigger correttamente

L'opzione di bilanciamento è l'opzione martello contro volo, non vi è alcun motivo per cui sia necessario più di un server per il conteggio di tali tps bassi.

Tuttavia, è necessario risolvere le prestazioni del trigger e verificare che non si stia eseguendo la congestione I/O da qualche parte (in genere ciò che è inutilmente lento tende a sfruttare troppo le risorse preziose).

Va bene, qui è un'altra opzione:

UNLOCK TABLES rilascia esplicitamente eventuali blocchi di tabella in possesso della sessione corrente.

se l'ultima azione è quella di aggiornamento/inserimento e se la vostra mancanza di innesco è impossibile o non è un problema

allora si potrebbe utilizzare questo all'inizio del grilletto, rilasciando tutte le serrature e chiedendo solo per il lettura coerente non bloccante.

1

aggiornamento e inserimento in mysql bloccato e syncronized opration, si supponga di avere 2 richieste provenienti da 2 trigger per l'aggiornamento della tabella D, quando 1 sta aggiornando la tabella D il secondo è atteso in coda. Per selezionare non esiste un blocco sincronizzato 2 thread può richiedere allo stesso tempo. Se si desidera effettuare questa transazione nello stesso tempo, è necessario creare la replica