doppelte SQL-Abfrage 100%-tig verhindern - Gewinnspiel
- gn#36
- Ehrenadmin
- Beiträge: 9313
- Registriert: 01.10.2006 16:20
- Wohnort: Ganz in der Nähe...
- Kontaktdaten:
Naja bei meiner 2. Methode wird überhaupt kein Select benötigt, ich gehe davon aus, dass in die in mySql vorhandenen Vorkehrungen, einen doppelten Index zu verhindern ausreichen. Ich lasse von allen Skripten in die gleiche Tabelle den selben Wert im Index eintragen, da kann nur einer der schnellste sein, bei allen anderen schlägt der Insert Befehl fehl was sich am Rückgabewert der Funktion überprüfen lässt.
Sollte das nicht ausreichend sein würde das bedeuten, das mysql Tabellen nicht indexsicher sind, das kann ich mir ehrlich gesagt nicht vorstellen.
Sollte das nicht ausreichend sein würde das bedeuten, das mysql Tabellen nicht indexsicher sind, das kann ich mir ehrlich gesagt nicht vorstellen.
Begegnungen mit dem Chaos sind fast unvermeidlich, Aber nicht katastrophal, solange man den Durchblick behält.
Übertreiben sollte man's im Forum aber nicht mit dem Chaos, denn da sollen ja andere durchblicken und nicht nur man selbst.
Übertreiben sollte man's im Forum aber nicht mit dem Chaos, denn da sollen ja andere durchblicken und nicht nur man selbst.
Mach bitte mal ein "Beispiel", wie Du das meinst.
Also eine Tabelle mit
gewinn_id user_id
Dann einen?
INSERT INTO tabelle (gewinn_id, user_id) VALUES ($gewinn_id, $user_id)
Du meinst sicher was anderes.
Gruß
Also eine Tabelle mit
gewinn_id user_id
Dann einen?
INSERT INTO tabelle (gewinn_id, user_id) VALUES ($gewinn_id, $user_id)
Du meinst sicher was anderes.
Gruß
meine Foren: http://www.maxrev.de/communities.htm
Ich kaufe Dein Forum! Angebote bitte an marc at gutt punkt it
Ich kaufe Dein Forum! Angebote bitte an marc at gutt punkt it
- Emanuelle_1982
- Mitglied
- Beiträge: 535
- Registriert: 06.03.2006 18:37
- Wohnort: Nümbrecht & Siegen
- Kontaktdaten:
bei meiner Lösung nicht... (siehe unten beim edit)mgutt hat geschrieben:Ihr vergesst, dass es passieren kann (in der Millesekunde), dass nahezu synchron mehrere User den ersten SELECT ausführen können, bevor beim allerersten User der INSERT fertig ist.
D.h. es wird schon nach und nach abgearbeitet, aber in der Zeit zwischen den beiden Abfragen, kann es durchaus sein, dass noch ein SELECT eines anderen durchhuscht.
Ich hatte das bereits mehrmals in einem extrem frequenten Thema. Dort "beschwerten" sich User, dass ihr Beitrag erst auf Seite X und dann auf Seite Y angezeigt wurde. Weil dort ein weiterer User parallel geschrieben hat (INSERT) und dieser aber noch nicht vom SELECT erfasst werden konnte.
ERST wird insert in 'soll-Seite-sehen' gemacht. erst DANACH wird in der GLEICHEN PHP datei nach der niedrigsten Tabellen ID gefragt.
Selbst wenn 2 user gleichzeitig drücken, wird nur einer die niedrige ID bekommen, darum kümmert sich mySQL
durch die Select Abfrage nach dem insert auf die gleiche Tabelle weisst du anschliessend wer als erster war (wie gesagt, die ID's werden von MySQL generiert - und nur einmal vergeben - das heisst es gibt immer eine niedrige ID, und NUR DIE hat gewonnen - alle anderen haben zwar den insert ausgeführt - aber eine höhere id und dürfen die Seite oder das Gewinsspiel NICHT sehen
also:
entscheidend ist bei mir nicht das insert, sondern das select von dem insert
das ist 100% sicher
von Sonderfällen wie Hacker angriff, mutwillige Änderung der Datenbank, Serverabstürze, etc abgesehen - aber ich glaube die darf man hier vernachlässigen
EDIT:
Bsp mit 2 Usern u1 und u2, Gewinnspiel g1
u1 findet Gewinnspiel
u2 findet Gewinnspiel
u1 trägt ein als 'soll-sehen'
(ID = 1 [mysql autoincrement], u1, g1)
u2 trägt ein als 'soll-sehen'
(ID = 2 [mysql autoincrement], u2, g1)
u1 macht select, Limit 1, orderBy ID wo gewinnspiel g1 auf 'sollen-sehen'
Eintrag: 1, u1, g1
hat gewonnen
u2 macht select, Limit 1, orderBy ID wo gewinnspiel g1 auf 'sollen-sehen'
Eintrag: 1, u1, g1
hat verloren, trotz insert !!
hiernach kann gewinnspiel g1 deaktivert werden damit nicht noch mehr inserts kommen
möglichkeit: trage als Gewinner - aber erst jetzt u1 ein
lösche hiernach alle einträge mit g1 aus der Tabelle 'soll-sehen'
Zuletzt geändert von Emanuelle_1982 am 04.02.2007 13:29, insgesamt 1-mal geändert.
- gn#36
- Ehrenadmin
- Beiträge: 9313
- Registriert: 01.10.2006 16:20
- Wohnort: Ganz in der Nähe...
- Kontaktdaten:
Ok bleiben wir bei deinem Beispiel. Wichtig ist allerdings dass bei der Tabelle der Primärschlüssel auf gewinn_id liegt.
Außerdem muss die Variable $gewinn_id die du verwendest für alle Personen die an demselben Gewinnspiel teilnehmen gleich sein.
Wenn jetzt 2 Personen gleichzeitig versuchen ihre Id als Gewinner des Spiels einzutragen dann muss eine Person von beiden eine Fehlermeldung bekommen.
Um dein Beispiel noch mal zu vervollständigen:
Tabelle mit gewinn_id (PRIMARY), user_id
Dann einen
INSERT INTO tabelle (gewinn_id, user_id) VALUES (1, $user_id)
den alle versuchen auszuführen. Wenn Mysql richtig funktioniert sollte das - da auf gewinn_id ein Primärschlüssel liegt - nur bei einer einzigen Person funktionieren.
Das lässt sich natürlich dann erweitern, wenn man z.B. den 1. 2. und 3. Platz braucht, dann gibt es noch eine weitere Spalte, in der der Platz gespeichert wird, der dann ebenfalls zum Primärschlüssel hinzugehören muss (so dass die Kombination aus gewinn_id und rang immer eindeutig sein muss). Dann können alle User nacheinander alle Insertbefehle ausprobieren (bei Erfolg natürlich die anderen nicht mehr) also in etwa so:
Außerdem muss die Variable $gewinn_id die du verwendest für alle Personen die an demselben Gewinnspiel teilnehmen gleich sein.
Wenn jetzt 2 Personen gleichzeitig versuchen ihre Id als Gewinner des Spiels einzutragen dann muss eine Person von beiden eine Fehlermeldung bekommen.
Um dein Beispiel noch mal zu vervollständigen:
Tabelle mit gewinn_id (PRIMARY), user_id
Dann einen
INSERT INTO tabelle (gewinn_id, user_id) VALUES (1, $user_id)
den alle versuchen auszuführen. Wenn Mysql richtig funktioniert sollte das - da auf gewinn_id ein Primärschlüssel liegt - nur bei einer einzigen Person funktionieren.
Das lässt sich natürlich dann erweitern, wenn man z.B. den 1. 2. und 3. Platz braucht, dann gibt es noch eine weitere Spalte, in der der Platz gespeichert wird, der dann ebenfalls zum Primärschlüssel hinzugehören muss (so dass die Kombination aus gewinn_id und rang immer eindeutig sein muss). Dann können alle User nacheinander alle Insertbefehle ausprobieren (bei Erfolg natürlich die anderen nicht mehr) also in etwa so:
Code: Alles auswählen
if(!mysql_query('INSERT INTO tabelle (gewinn_id, rang, user_id) VALUES (1,1, $user_id)'))
{
if(!mysql_query('INSERT INTO tabelle (gewinn_id, rang, user_id) VALUES (1,2, $user_id)'))
{
if(!mysql_query('INSERT INTO tabelle (gewinn_id, rang, user_id) VALUES (1,3, $user_id)'))
{
echo "Tja, da war jemand schneller...";
}
}
}
Begegnungen mit dem Chaos sind fast unvermeidlich, Aber nicht katastrophal, solange man den Durchblick behält.
Übertreiben sollte man's im Forum aber nicht mit dem Chaos, denn da sollen ja andere durchblicken und nicht nur man selbst.
Übertreiben sollte man's im Forum aber nicht mit dem Chaos, denn da sollen ja andere durchblicken und nicht nur man selbst.
An einen Mehrkernprozessor habe ich nicht gedacht der ist/sind aber im Grunde auch zwei Prozesoren. Auf eine Speicherzelle können Sie aber auch nicht gleichzeitig zugreifen. In die "Pause" zwischen dem select und dem nächten Zugriff könnte aber ein anderer Prozess reinhauen.gn#36 hat geschrieben:Warum sollte ein Server nicht mit mehreren Prozessen gleichzeitig auf eine Datenbank zugreifen? Genau das ist doch die Idee bei Mehrkernprozessoren, ...
Das sagtest Du bereits. Sollen die Inserts doch ausgeführt werden, gewonnen hat der Erste in der Tabelle. Und sobald die ersten selects was ausspucken ist mit den Eintragungen schluß.mgutt hat geschrieben:Ihr vergesst, dass es passieren kann (in der Millesekunde), dass nahezu synchron mehrere User den ersten SELECT ausführen können, bevor beim allerersten User der INSERT fertig ist.
D.h. es wird schon nach und nach abgearbeitet, aber in der Zeit zwischen den beiden Abfragen, kann es durchaus sein, dass noch ein SELECT eines anderen durchhuscht.
mein vorschlag..
erstelle tabelle gewinnspiel
mit den feldern: id(autoincrement), username
nun wenn man die seite aufruft, wird ein select befehl ausgeführt ob schon ein eintrag mit der id 1 drinne ist... wenn nicht wird der user eingetragen...
wenn ja, dann wird abgebrochen mit einer fehlermeldung: es tut uns leid aber ein anderes mitglied hat leider schon gewonnen...
oder hab ich da was falsch verstanden?
erstelle tabelle gewinnspiel
mit den feldern: id(autoincrement), username
nun wenn man die seite aufruft, wird ein select befehl ausgeführt ob schon ein eintrag mit der id 1 drinne ist... wenn nicht wird der user eingetragen...
wenn ja, dann wird abgebrochen mit einer fehlermeldung: es tut uns leid aber ein anderes mitglied hat leider schon gewonnen...
oder hab ich da was falsch verstanden?
Also nach Xwitz Kommentare verstehe ich es so.. erst SELECT auf mögliche Gewinner, wenn nicht (das kann ruhig parallel durch mehrere passieren) ein INSERT. Danach wieder ein SELECT auf den niedrigsten autoincrement-Wert einer gewinnspiel_id. Und wenn dort die User_id mit der des Besuchers gleicht, dann sollte man es haben.
Also:
SELECT (nur true, wenn noch kein INSERT vorher war)
wenn true, dann INSERT
SELECT auf den Gewinner (true, wenn gewinner_id = user_id)
Auch praktikabel denke ich
Also:
SELECT (nur true, wenn noch kein INSERT vorher war)
wenn true, dann INSERT
SELECT auf den Gewinner (true, wenn gewinner_id = user_id)
Auch praktikabel denke ich

meine Foren: http://www.maxrev.de/communities.htm
Ich kaufe Dein Forum! Angebote bitte an marc at gutt punkt it
Ich kaufe Dein Forum! Angebote bitte an marc at gutt punkt it
- Jan500
- Ehemaliges Teammitglied
- Beiträge: 4199
- Registriert: 01.03.2003 21:32
- Wohnort: Hamburg
- Kontaktdaten:
du kannst auch nach dem ersten insert die id ausgeben mit $id = mysql_insert_id();mgutt hat geschrieben:Also:
SELECT (nur true, wenn noch kein INSERT vorher war)
wenn true, dann INSERT
SELECT auf den Gewinner (true, wenn gewinner_id = user_id)
dann beim zweitem select prüfen ob die insert id mit der gefilterten (also dem ersten eintrag, am besten mit nem timestamp und dem niedrigsten auto_increment wert)) übereinstimmt so brauchst du nicht mit der user_id arbeiten...
Jan
"Life begins at 40 Knots...!" 
kein (kostenlosen) Support per pn, mail, icq usw. | Kostenlosen Support gibt es hier im Forum!

kein (kostenlosen) Support per pn, mail, icq usw. | Kostenlosen Support gibt es hier im Forum!
Die user_id muss ich haben. Die brauche ich für die Gewinnerliste. Sonst weiß ich ja gar nicht wer gewonnen hat.
Gruß
Gruß
meine Foren: http://www.maxrev.de/communities.htm
Ich kaufe Dein Forum! Angebote bitte an marc at gutt punkt it
Ich kaufe Dein Forum! Angebote bitte an marc at gutt punkt it