2012-03-21 17 views
6

Sto aiutando un amico con un modulo basato sul web che è per il loro business. Sto cercando di renderlo pronto per gestire più utenti. L'ho impostato in modo che appena prima che il record venga visualizzato per la modifica, sto bloccando il record con il seguente codice.mysql row locking via php

$query = "START TRANSACTION;"; 
mysql_query($query); 
$query = "SELECT field FROM table WHERE ID = \"$value\" FOR UPDATE;"; 
mysql_query($query); 

(va bene che è notevolmente semplificata, ma che è l'essenza del mysql)

Non sembra funzionare. Tuttavia, quando vado direttamente a MySQL da linea di comando, entrando con lo stesso utente ed eseguire

START TRANSACTION; 
SELECT field FROM table WHERE ID = "40" FOR UPDATE; 

posso bloccare in modo efficace il modulo web di accedere ai record "40" e ottenere l'avviso di timeout.

Ho provato a utilizzare BEGIN anziché START TRANSACTION. Ho provato a fare SET AUTOCOMMIT = 0 prima e avviare la transazione dopo il blocco, ma non riesco a bloccare la riga dal codice PHP. Dal momento che posso bloccare la riga dalla riga di comando, non penso che ci sia un problema con il modo in cui il database è impostato. Spero davvero che ci sia qualcosa di semplice che mi è sfuggito nella mia lettura.

FYI, Sto sviluppando su XAMPP versione 1.7.3 che ha Apache 2.2.14, MySQL 5.1.41 e PHP 5.3.1.

Grazie in anticipo. Questa è la mia prima pubblicazione, ma in passato ho raccolto molte informazioni su questo sito.

+0

Cosa hai fatto dopo la seconda mysql_query ($ query); ?se il tuo script php termina, quindi apri il blocco – PasteBT

risposta

0

Usa DOP per questo (e di tutte le operazioni del database):

$value = 40; 

try { 
    $dbh = new PDO("mysql:host=localhost;dbname=dbname", 'username', 'password'); 
} catch (PDOException $e) { 
    die('Could not connect to database.'); 
} 

$dbh->beginTransaction(); 

try { 
    $stm = $dbh->prepare("SELECT field FROM table WHERE ID = ? FOR UPDATE"); 
    $stm->execute(array($value)); 
    $dbh->commit(); 
} catch (PDOException $e) { 
    $dbh->rollBack(); 
} 

se è necessario utilizzare il mysql_ antiquata * funzioni che è possibile una cosa del genere:

mysql_query('SET AUTOCOMMIT=0'); 
mysql_query('START TRANSACTION'); 
mysql_query($sql); 
mysql_query('SET AUTOCOMMIT=1'); 
+2

Non downvotare in silenzio. Lascia un commento per aiutare le risposte migliori. – webbiedave

+2

Per la cronaca non vedo alcun problema con questo suggerimento, è lungimirante! – Paul

0

Si consiglia di non utilizzare l'API di MySQL perché è facile commettere errori che consente a SQL-iniezioni e simili, così come manca di alcune funzionalità. Sospetto che la transazione sia uno di questi perché se non sbaglio ogni query viene inviata "da sola" e non in un contesto più ampio.

La soluzione tuttavia è utilizzare qualche altra API, preferisco mysqli perché è così simile a mysql e ampiamente supportata. Puoi facilmente riscrivere il tuo codice per usare mysqli come bene.

Per la funzionalità della transazione, impostare auto-commit su false e commetterlo quando lo si desidera. Questo equivale all'avvio e all'arresto delle transazioni.

Per il look di riferimento all'indirizzo:

http://www.php.net/manual/en/mysqli.autocommit.php

http://www.php.net/manual/en/mysqli.commit.php

9

Il problema non è la sintassi del codice, ma il modo in cui si sta tentando di utilizzare esso.

appena prima che il record viene visualizzato per la modifica sto di blocco il record con il seguente codice

Da questo io parto dal presupposto che si seleziona e "bloccare" la riga, quindi visualizzare la pagina di modifica per il tuo utente, quindi quando inviano le modifiche salva e "sblocca" la tabella. Qui sta il problema fondamentale. Al termine del caricamento della pagina, il PHP chiude e chiude la connessione MySQL. Quando ciò accade, tutte le serrature vengono immediatamente rilasciate. Questo è il motivo per cui la console sembra comportarsi diversamente dal tuo PHP. L'equivalente nella console sarebbe l'uscita dal programma.

Non è possibile bloccare le righe della tabella per la modifica per un periodo prolungato. Questo non è il loro design. Se si desidera bloccare un record per la modifica, è necessario tenere traccia di questi blocchi in un'altra tabella. Crea una nuova tabella chiamata "edit_locks" e salva l'ID del record bloccato, la modifica dell'ID utente e il tempo in cui è stato bloccato. Quando si desidera aprire un record per la modifica, bloccare l'intera tabella edit_locks e interrogare per vedere se il record è bloccato da qualcun altro. In caso contrario, inserire il record di blocco, se lo è, quindi visualizzare un errore bloccato. Quando l'utente salva o annulla, rimuovi il record di blocco da edit_locks. Se vuoi semplificare le cose, blocca questo tavolo ogni volta che il tuo programma vuole usarlo. Questo ti aiuterà ad evitare una condizione di gara.

C'è uno scenario in più che può causare un problema. Se l'utente apre un record per la modifica, quindi chiude il browser senza salvare o annullare, il blocco modifica rimarrà lì per sempre. Questo è il motivo per cui ho detto di conservare il tempo in cui è stato bloccato. L'editor stesso dovrebbe effettuare una chiamata AJAX ogni 2 minuti o così per dire "Ho ancora bisogno del blocco!". Quando il programma PHP riceve questa richiesta "relock", dovrebbe cercare il blocco, quindi aggiornare il timestamp al corrente. In questo modo il timestamp sulla serratura è sempre aggiornato entro 2 minuti. È inoltre necessario creare un altro programma per rimuovere i vecchi blocchi bloccati. Questo dovrebbe essere eseguito in un cron job ogni pochi minuti. Dovrebbe cercare qualsiasi blocco con un timestamp più vecchio di 5 minuti o così, e rimuovere. Se il timestamp è più vecchio di quello, allora chiaramente l'editor era vicino a come o il timestamp sarebbe aggiornato entro 2 minuti.

Come alcuni degli altri hanno menzionato, si dovrebbe provare a utilizzare mysqli. Sta per "MySQL Improved" ed è la sostituzione per la vecchia interfaccia.

+0

Questo modo di fare ha alcuni vantaggi ma anche alcuni svantaggi. Di tanto in tanto avrai bisogno di una pulizia per il tuo tavolo di blocco/sblocco. Questo è causato da arresti anomali o distruzioni impreviste. Io personalmente non lo userei. – Andreas

+0

Ok, JMack, hai esteso la tua risposta ora e spiegato gli svantaggi nella tua seconda parte. Risposta solida +1 – Andreas

+0

@Andreas Incluso nella mia descrizione è un metodo per pulire la tabella di blocco in modo che qualsiasi arresto anomalo del browser causi un blocco della registrazione di non più lungo di alcuni minuti. –

0

Questa è una vecchia discussione, ma forse la gente lo sta ancora seguendo. Io uso un metodo simile a quello di JMack ma includo le informazioni di blocco nella tabella che voglio bloccare a riga. Le mie nuove colonne sono LockTime e LockedBy. Per tentare una serratura, lo faccio:

UPDATE table 
SET LockTime='$now',LockedBy='$Userid' 
WHERE Key='$value' AND (LockTime IS NULL OR LockTime<'$past' OR LockedBy='$Userid') 

($ passato è 4 minuti fa)

Se questo non funziona, qualcun altro ha la serratura. Posso esplicitamente sbloccare la seguente o lasciare che il mio blocco scade:

UPDATE table 
SET LockTime=NULL,LockedBy='' 
WHERE Key='$value' AND LockedBy='$Userid' 

Un lavoro cron potrebbe rimuovere vecchie serrature, ma non è davvero necessario.