2013-02-28 9 views
20

Una domanda in due parti:In che modo l'ultimo ID inserto di mysql funziona con le transazioni? + Domande di transazione

  1. Nel mio script CodeIgniter, sto iniziando una transazione, poi l'inserimento di una riga, impostando l'insert_id() a una variabile PHP, l'inserimento di più righe in un'altra tabella usando il nuovo ID come chiave esterna, e poi commetto tutto.

    Quindi la mia domanda è: se tutto non si impegna prima di terminare la transazione, in che modo mysql è in grado di restituire l'ultimo ID di inserimento, se non è stato inserito nulla? Il mio script funziona (quasi) perfettamente, con il nuovo ID utilizzato nelle query successive.

    (Dico "quasi" perché, usando il driver mysql PDO, a volte il primo inserto che deve restituire l'insert_id() è duplicato - viene inserito due volte. Qualche idea sul perché sarebbe? ? per ottenere l'ultimo ID non accade mai se si utilizza il mysqli o il driver mysql)

  2. ho scritto la sceneggiatura, senza transazioni, quindi devo codice che controlla per errori di MySQL lungo la strada, come ad esempio:.

    if(!$this->db->insert($table, $data)) { 
        //log message here 
    } 
    

    In che modo questo influenza il processo mysql una volta che ho avvolto tutto il mio codice mysql in una transazione? Non sta causando alcun errore visibile (si spera non correlato al problema sopra indicato), ma dovrebbe essere rimosso?

Grazie.

risposta

27

Per rispondere alla tua prima domanda ...

Quando si utilizzano le transazioni, le query vengono eseguite normalmente per quanto riguarda la vostra connessione è interessato. È possibile scegliere di eseguire il commit, salvare tali modifiche o eseguire il rollback, ripristinando tutte le modifiche. Si consideri il seguente pseudo-codice:

insert into number(Random_number) values (rand()); 
select Random_number from number where Number_id=Last_insert_id(); 

// php

if($num < 1) 
    $this->db->query('rollback;'); // This number is too depressing. 
else 
    $this->db->query('commit;'); // This number is just right. 

Il numero casuale che è stato generato può essere letto prima di impegnarsi per assicurare che sia adatto prima di salvarla per tutti da vedere (ad esempio, commetti e sblocca la riga).

Se il driver PDO non funziona, si consiglia di utilizzare il driver mysqli. Se questa non è un'opzione, puoi sempre utilizzare la query 'select last_insert_id() come id;' piuttosto che la funzione $ this-> db-> insert_id().

Per rispondere alla seconda domanda, se si stanno inserendo o aggiornando dati che altri modelli aggiorneranno o leggeranno, assicurarsi di utilizzare le transazioni. Ad esempio, se una colonna "Number_remaining" è impostata su 1, può verificarsi il seguente problema.

Person A reads 1 
Person B reads 1 
Person A wins $1000! 
Person A updates 1 to be 0 
Person B wins $1000! 
Person B updates 0 to be 0 

Utilizzando le operazioni nella stessa situazione darebbe questo risultato:

persona A inizia transazione
persona A si legge '1' da NUMBER_REMAINING
(La fila è ora bloccata Se viene utilizzato select for update)
Persona B tenta di leggere Number_remaining - forzato di attendere
Persona A vince $ 1000
Persona A aggiornamenti 1 essere 0
Persona A imposta
persona B legge 0
persona B non Vinci $ 1000
persona B grida

Si consiglia di leggere su transaction isolation levels pure.

Fate attenzione di stallo, che può verificarsi in questo caso:

persona Una legge riga 1 (select ... for update)
persona B legge fila 2 (select ... for update)
Persona A tenta di leggere riga 2, costretti ad attendere
persona B tenta di leggere riga 1, costretti ad attendere
persona a raggiunge innodb_lock_wait_timeout (50sec default) ed è scollegato
persona B legge riga 1 e continua normalmente

Alla fine, dal momento che la persona B ha probabilmente raggiunto di PHP max_execution_time, la query corrente terminerà l'esecuzione in modo indipendente di PHP, ma sarà ricevuto ulteriori domande. Se si trattava di una transazione con autocommit = 0, la query verrà automaticamente ripristinata quando viene interrotta la connessione al server PHP.

+1

Grazie per la risposta! Solo per essere sicuro di aver capito correttamente, intendi dire che se avessi avvolto tutte le query mysql in una transazione, e lo scenario che hai scritto sopra accadesse (due utenti accederanno entrambi a una pagina "vincente" allo stesso tempo), allora passare solo per una persona? Avrei bisogno di controllare se 'number_remaining' è uguale a 1 all'inizio della transazione, però, corretto? –

+1

corretto. Per l'esempio che ho fornito, dovresti anche assicurarti che il tuo livello di isolamento sia impostato su serializzabile o che sia stato usato un "select for update" per bloccare la riga su select. Le istruzioni di aggiornamento, cancellazione e inserimento hanno un comportamento di blocco diverso a seconda del livello di isolamento. L'utilizzo di transazioni per ogni query può portare a deadlock, quindi usali quando è di vitale importanza che i tuoi dati siano aggiornati e accurati. Ho aggiornato la mia risposta per includere un esempio con transazioni abilitate. –

+2

Grazie mille, questa è un'ottima spiegazione! Non ero a conoscenza di quelle opzioni mysql che hai menzionato. Ho ancora un paio di domande, se non ti dispiace: 1. Ho cercato 'serializable' nella documentazione di mysql, e mostra che imposta un' lock in share mode'. Questo in realtà non impedisce di selezionare la riga. Penso che "seleziona ... per l'aggiornamento" dovrebbe essere usato, corretto (blocca la lettura)? 2. In quali casi può esserci un deadlock usando le transazioni? Perché le transazioni successive non dovrebbero semplicemente aspettare il loro turno? –