2016-07-13 13 views
1

Ich bin neu in MySQL und ich habe viele Post-Talking-Transaktionen und Tabellensperren gelesen, die ich hier und über das Netz gefunden habe, also denke ich, dass meine Frage nicht redundant sein sollte.MySQL Transaktion VS Table Lock für Benutzerregistrierung

Ich versuche, Abfragen zu optimieren, vor allem für Benutzerregistrierung und Sitzungen. Die Webanwendung ist in PHP/MySQL (i) geschrieben und ich verwende InnoDB Engine.

Ich benutze $ _SESSIONS nicht, um Benutzersitzungen zu speichern, aber ich benutze eine Tabelle auf DB, in der ich einige Informationen über die Benutzersitzung speicher, die mit Plätzchen lebendig gehalten wird. Diese Methode erfordert, dass ich die Benutzersitzung in der DB für jede Benutzerseitenanforderung überprüfe. Dadurch schließe ich mich auch der "users" -Tabelle in der Select-Session-Abfrage an, um jedes Mal neue Benutzerinformationen zu erhalten.

Auf Benutzerregistrierung Anfragen bin ich nicht sicher, was Methode ist besser für: Performance, Codequalität und Sicherheit Probleme zu vermeiden, wenn mehr als ein Benutzer versuchen, die gleichen Benutzername/E-Mail zur gleichen Zeit zu registrieren.

Benutzer Tabelle:

user_id (primary, AI) 
username (unique) 
email (unique) 
password 
field 1 
field 2 
... 

Wenn Abfrage schlägt fehl, da der Benutzername oder E-Mail bereits Ich muss wissen genommen wird, zu dem man bereits vergeben ist.

VERFAHREN: LOCK Benutzer TABLE

LOCK TABLES users WRITE; 
// users provided username and email are already taken? 
SELECT COUNT(username) username, (SELECT COUNT(email) FROM users WHERE email = '[email protected]') email FROM users WHERE username = 'batman' 

$res = fetch_array; 
if($res['username'] == 0 && $res['email'] == 0){ 
    INSERT INTO users (username,email,password) VALUES ('batman','[email protected]','imtheonlytruehero') 

    if(affected_rows == 1) 
     $username = null; 
     $email= null; 
     $registration = true; 
    } 
    else { 
     $username = null; 
     $email= null; 
     $registration = false; 
    } 
} 
else { 
    $username = ($res['username'] > 0) ? false : true; 
    $email = ($res['email'] > 0) ? false : true; 
    $registration = false; 
}  
UNLOCK TABLES; 

METHODE B: TRANSACTION mit INSERT + UPDATE

START TRANSACTION; 
INSERT INTO users (username,email,password) VALUES ('batman','0','imtheonlytruehero'); 

if(affected_rows > 0) { 
    $username = true; 
    UPDATE users SET email='[email protected]' WHERE username='batman'; 
    if(affected_rows > 0) { 
     $email = true; 
     $registration = true; 
     COMMIT; 
    } 
    else { 
     $email = false; 
     $registration = true; 
     ROLLBACK; 
    } 
} 
else { 
    $username = false; 
    $email = null; 
    $registration = false; 
    ROLLBACK; 
} 

Meine Sorge Methode A verwendet, ist: Was ist mit anderen protokollierten geschieht Benutzer (unter der Annahme, dass sie viel sind), wenn sie versuchen, ihre Sitzungen für die Navigation verifiziert zu bekommen (erinnern Sie sich an "Sitzungsüberprüfung", schließen Sie sich auch der Benutzertabelle an) whe n Ein neuer Benutzer registriert sich und seine Abfrage sperrt die Benutzertabelle? Langsamkeit? Zeitüberschreitungen? Oder sind die Abfragen (auswählen und einfügen) leicht genug, um die Leistung nicht zu beeinträchtigen?

Hat Methode B hohe Chancen, Deadlocks zu generieren?

Was kann ich tun? Wählen Sie eine dieser Methoden? Mischen sie? Trashing alles und neu anfangen?

Ich schätze jede Hilfe. Danke.

+0

Was Sie wirklich nur einen eindeutigen Index tun sollte, ist, setzen auf den Benutzernamen und E-Mails. Dann können Sie überhaupt keine Duplikate haben, und die Datenbank übernimmt das für Sie. Transaktionen werden keine doppelten Werte blockieren, und die Sperrung würde wahrscheinlich funktionieren, könnte aber Probleme verursachen, wenn nichts getan werden kann, während die Tabelle gesperrt ist, was die Leistung beeinträchtigt. Ein eindeutiger Index würde doppelte Zeilen verhindern und kann als Ausnahme abgefangen werden. –

+0

Danke für Ihre Antwort. Ich habe bereits einen eindeutigen Index für den Benutzernamen und die E-Mail-Spalten festgelegt, wie am Anfang des Beitrags erwähnt. Mein Zweifel ist jedoch, wenn ich nur die Einfügung und ein Benutzer benutze sowohl Benutzernamen und E-Mail bereits auf der Tabelle gesetzt kann ich ihm nicht sagen, wenn beide genommen werden, weil mysql Fehlerbericht nur der erste doppelte Schlüssel aufgetreten ist, so dass ich in diesem Fall kann Ich weiß nicht, ob auch die E-Mail bereits verwendet wird, was dazu führt, dass der Benutzer eine andere Einfügung versucht, um zu wissen, dass auch die E-Mail genommen wurde. Kennen Sie irgendeine Methode, um sich diesem "Problem" zu stellen? Vielen Dank! – cicciopast

+0

Abfrage, um zu sehen, ob entweder bereits in der Datenbank vorhanden sind. dann kannst du ihnen sagen, dass sie benutzt werden. Der eindeutige Index ist nur dazu da, um sicherzustellen, dass es keine Duplikate gibt. nichts anderes. Sie sollten im Voraus überprüfen, ob es benutzerfreundlicher ist und sich nicht auf den Datenbankindex verlässt, um die Fehler zu erkennen. Sie sind ein Sicherheitsnetz. –

Antwort

0

Das Sperren einer ganzen Tabelle sollte nur in Ausnahmefällen verwendet werden, METHODE A würde die Parallelität behindern. Unter der Annahme, dass Sie sowohl eindeutige Indizes auf Benutzername haben und E-Mail METHODE B zum Beispiel neu geschrieben werden könnte weiter unten.

isolation modes, in MySQL und PostgreSQL (Pseudo-Code) in beiden READ COMMITTED und WIEDERHOLBARE READ arbeiten Dieser Ansatz sollte:

while (1) { 
    START TRANSACTION; 
    SELECT * FROM users WHERE username = ? OR email = ?; 

    if (something_found) { 
     // figure out what was found — email, login or both 
     // ... 
    } else { 
     try { 
      INSERT INTO users …; 
     } catch (UniqueViolation) { 
      // somebody else could have inserted a record with the same 
      // username/email after our SELECT query 
      ROLLBACK; 
      // try again 
      continue; 
     }   
    } 
    COMMIT; 
    break; 
} 
0

Sperren Tabellen, wenn möglich, vermieden werden sollte. Für Methode A gibt es keinen Grund, etwas zu sperren oder Transaktionen zu verwenden.

Dies ist, wie Sie Methode A (beachten Sie, dass, wenn Sie Benutzerdaten einfügen, sollten Sie vorbereitete Anweisungen verwenden) behandeln würde

$result = $db->query('SELECT COUNT(id) as total FROM USER WHERE username = "batman" or email = "[email protected]"'); 
$row = $result->fetch_assoc(); 
if($row['total'] > 0}{ 
    // redirect back to form, exit here 
} 

try{ 
    // no user with email, username exists, so try to insert that user 
    $db->query('INSERT INTO USER ...'); 
    $registration = true; 
} 
catch(Exception e){ 
    // If a different user with same username or email registered in 
    // the mean time then redirect to form. 
}