mmofacts.com

DB-Abfrage verloren?

gepostet vor 15 Jahre, 2 Monate von D4rk5in

Hi,

ich dachte ich frag mal euch. Und zwar gibt es da einige Probleme, die man erst kennenlernt, wenn eine bestimmte Anzahl von Usern eine Seite regelmäßig besucht. Zur Zeit rätsel ich da an einem etwas seltsamen Problem.

Die Sache ist so: Mittlerweile kommt es ab und zu vor, dass bei einer Anmeldung der Eintrag für die Rohstoffe einfach nicht getätigt wird. Bei den meisten Anmeldungen gibt es jedoch keine Probleme - alle Einträge in der DB werden erstellt.

Nun die Frage: Woran kann das liegen?

Lange Zeit bin ich davon ausgegangen, dass es am Server liegt (der alte war ziemlich überlastet). Aber auf dem neuen Server, der spürbar schneller ist und deutlich mehr Leistung liefert, besteht das Problem nun immer noch. Das Problem ist auch erst aufgetaucht, als die Zahl der User, die online sind, fast konstant zwischen 20 und 30 waren. 

In den Logfiles des Datenbankservers (MySQL) finden sich jedoch keine Fehler. Geht es überhaupt, dass Anfragen einfach so verloren gehen können? Wenn ja, unter welchen Umständen?

Ich hab zwar bereits die Config-Files meiner Server angepasst, aber evtl. gibts da doch einen Flaschenhals?

MfG

gepostet vor 15 Jahre, 2 Monate von buhrmi

Also deine vermutung ist, dass der mysql server einfach queries ignoriert? Ganz ohne Fehlermeldung? Wär ja ein interessantes "Feature". :P

Meine erste Idee bei sowas sind race conditions.

Original von D4rk5in

 Mittlerweile kommt es ab und zu vor, dass bei einer Anmeldung der Eintrag für die Rohstoffe einfach nicht getätigt wird.

Zeig mal den Code dazu

gepostet vor 15 Jahre, 2 Monate von Madman

Kann eigentlich nicht sein. Ich denke evtl. hast du MySQL-Server abbrüche oder so? Dass die DB wegkracht, wegen irgendwas? Kommt ab und zu bei schlecht gehosteten Webservern vor...

gepostet vor 15 Jahre, 2 Monate von D4rk5in

Das ist ja eben das, was mich verwundert...

Der Code macht Folgendes:

Eintrag in tbl_user

Generierte uid auslesen

-> Es folgen weitere Einträge in Tabellen, die mit der uid verknüpft werden, so auch der Rohstoffeintrag:

INSERT INTO tbl_rohstoffe (uid, geld, metall, ...) VALUES (A, B, C, ...)

Dadurch, das es ein INSERT-Befehl ist der nur ein einziges Mal an die DB gesendet wird, kann das mit den Race Conditions doch gar nicht der Grund sein, oder?

MfG

gepostet vor 15 Jahre, 2 Monate von Nuky

bei solchen sachen ist man oft verleitet an stellen zu suchen dies garnicht sein können. die db lasst sicher nicht eine abfrage fallen und macht sie nicht - eher wird sie nicht ausgeführt wegen einer verschachtelung, hat keine gültige id weil die nicht übergeben wird,...

gepostet vor 15 Jahre, 2 Monate von Madman

Hast du getestet, das das mit der uid auslesen funktioniert? Nur mal so als Ansatzidee :/ Bei nur einem Insert kann es nicht allzuviele Fehler geben :(

gepostet vor 15 Jahre, 2 Monate von D4rk5in

Jop, die uid wird ausgelesen. Alle anderen Einträge werden ja auch erstellt. Nur eben der Rohstoffeintrag manchmal nicht (ist bis jetzt ca. 5-10 Mal vorgekommen). Und das bei fast 500 Anmeldungen...

Die tbl_rohstoffe wird bei 20 Usern online eigentlich fast sekündlich vom Server ausgelesen und bearbeitet. Weil bei jedem Klick des Users werden auch die Rohstoffe neu berechnet. Die Tabelle ist also eigentlich ziemlich oft im wahrsten Sinne des Wortes "gefragt"...

Könnte damit was zutun haben, oder?

MfG

gepostet vor 15 Jahre, 2 Monate von TheUndeadable

Gibt es irgendwo ein Skript nach folgendem mit folgendem Schema:

foreach ( player in AlleSpieler )
{
rohstoffe = player->HoleRohstoffe();
Modifiziere(Rohstoffe);
player->SchreibeRohstoffe(rohstoffe);
}

Also:

Lesen, Verarbeiten, Schreiben...

gepostet vor 15 Jahre, 2 Monate von D4rk5in

Nicht für alle Spieler, aber für alle Basisstationen eines Spielers.

Z.B. in der Übersicht, die man sich anzeigen lassen kann. Da werden alle eigenen Basisstationen aktualisiert in einer Schleife.

MfG

gepostet vor 15 Jahre, 2 Monate von Todi42

Ignorierst Du irgend wo Fehlermeldungen der DB? Könnte sein, dass sie Dir "deadlock looser" zurück gibt und Du protokolierst das nicht entsprechend. Immer schön alle Returnwerte abfragen ;-)

gepostet vor 15 Jahre, 2 Monate von buhrmi

Dass nach einem INSERT kein neuer Datensatz vorhanden ist, kann nicht an einer race condition liegen. Stimmt.

Höchstwahrscheinlich wird die Query entweder nicht gar nicht ausgeführt (code-verschachtelung, code wäre nicht schlecht. Mir kommt die Formulierung "uid aus anderer tabelle auslesen und anschließend rohstoffe erstellen" eh merkwürdig vor), oder sie erzeugt einen Fehler. Hast du nen Exception notifier drin?

gepostet vor 15 Jahre, 2 Monate von D4rk5in

Original von buhrmi

Höchstwahrscheinlich wird die Query entweder nicht gar nicht ausgeführt (code-verschachtelung, code wäre nicht schlecht. Mir kommt die Formulierung "uid aus anderer tabelle auslesen und anschließend rohstoffe erstellen" eh merkwürdig vor), oder sie erzeugt einen Fehler. Hast du nen Exception notifier drin?

Nein, ich habe keinen Exception Notifier drin. Es ist nun mal so, dass das Skript schon hunderte Male funktioniert hat. Die "UID" hole ich ganz einfach:

mysql_query("INSERT INTO tbl_user ...);

$uid = mysql_insert_id();

mysql_query("INSERT INTO tbl_rohstoffe ...);

So sieht das vereinfacht aus... Abfragen gibt es davor keine (außer die Sicherheitsabfragen, wo geprüft wird, ob alles ausgefüllt wurde etc.) Ist so programmiert, dass der Code nur ausgeführt wird, wenn keine Sicherheitsabfragen false zurückliefern. Also entweder wird der ganze Code ausgeführt oder gar nichts... deswegen kann es nicht sein, dass z.B. nur die Rohstoffe eingetragen werden und was anderes nicht.

MfG

gepostet vor 15 Jahre, 2 Monate von buhrmi

poah keine Ahnung :o ... Bastel am besten einen Notifier dran der eine mail rausschickt wenn der query einen fehler wirft.

Wenn die Rohstoffdatenbank ehrlich weniger Datensätze enthält als die Userdatenbank, ohne dass da von Hand drin rumgewurschtelt oder gelöscht wurde, und diese beiden Queries IMMER gemeinsam ausgeführt werden, dann kann es sich mMn nur um einen Fehler im Query handeln. Der allerdings einfach übergangen wird da keine Fehlerbehandlung passiert.

So sieht das vereinfacht aus...

Dann zeig doch mal die wirklichen code :o vielleicht is da ja irgendwas drin

gepostet vor 15 Jahre, 2 Monate von TheUndeadable

> mysql_query("INSERT INTO tbl_user ...);
> $uid = mysql_insert_id();
> mysql_query("INSERT INTO tbl_rohstoffe ...);

Ui, ui, ui....

Keine DB-Handles und evtl noch Datenbank-Verbindungspooling... Könnte IMHO eine Run-Condition sein, da andere Threads deinen Datenbank-Handle nutzen und zwischen drin andere Inserts abfeuern...

gepostet vor 15 Jahre, 2 Monate von buhrmi

bei nur 20 usern schon? :O

gepostet vor 15 Jahre, 2 Monate von Madman

Naja wie schon gesagt kommt viel auf den Server an würde ich sagen, ob der noch anderweitig über- oder belastet ist.

und mysql_insert_id() hab ich noch nie vertraut , versuche doch mal:

Insert tbl_user set x=y, z=a [...]

select id from tbl_user WHERE x=y and z=a;

Evtl ist das sicherer, die werte verändern sich dabei ja nicht.....

gepostet vor 15 Jahre, 2 Monate von exe

Du solltest auf jedenfall die Datenbankfunktionen kappseln um einfacher bei allen mysql_*-Funktionen das richtige Datenbankhandle übergeben zu können. Dann bei allen Queries den Rückgabewert prüfen und im Zweifelsfalle Fehler auslösen. Wenn du dann keine Queries nach dem Motto "Fire and Forget" mehr hast: error_log in PHP aktivieren, display_errors deaktivieren (die User interessieren sich nicht für deine Notices/Warnings). Wenn dann das nächste mal ein Query nicht geklappt hat kannst du sowohl im MySQL- als auch PHP-Errorlog schauen.

Wenn der Query aus irgendeinem Grunde fehlschlägt solltest du dann auch eine Fehlermeldung irgendwo aufgezeichnet haben. Einfach so dürfte kein Query verschwinden. Hast du keinen Fehler im Log UND kann das Script nicht ohne Fehler vor dem Query abbrechen geht er wohl durch aber mit den falschen Daten. Dann musst du wohl oder übel in den sauren Apfel beissen und Stück für Stück den Scriptablauf mitloggen um herauszufinden, an welcher Stelle die Daten nicht mehr stimmen mit denen hantiert wird.

gepostet vor 15 Jahre, 2 Monate von HSINC

Original von TheUndeadable

> mysql_query("INSERT INTO tbl_user ...);
> $uid = mysql_insert_id();
> mysql_query("INSERT INTO tbl_rohstoffe ...);

Ui, ui, ui....

Keine DB-Handles und evtl noch Datenbank-Verbindungspooling... Könnte IMHO eine Run-Condition sein, da andere Threads deinen Datenbank-Handle nutzen und zwischen drin andere Inserts abfeuern...

  in php eher nein, in dem fall wird die letzte offene/geöffnete verbindung genommen und die wird eigentlich auch net geshared

ich würd die insert query mal mit nem $resukt=mysql.. ausstatten und wenn false, fann eben mal alles mitloggen was da passierte also mysql errors etc

gepostet vor 15 Jahre, 2 Monate von knalli

Original von TheUndeadable

http://www.php.net/manual/de/function.mysql-insert-id.php#81129

Race Conditions können schon bei zwei Nutzern auftreten...

Genau wie man Lotto schon beim ersten Spiel gewinnen kann.

Natürlich können Race Conditions auch bei 2 auftreten, wie auch 2er-Rennen Sinn machen. Ich glaube, die eigentliche Kernaussage der Frage war: Wie kann ein 20-Mann-Spiel zu häufigen/bemerkbaren Ausfällen führen? A) Server ist doch für'n Arsch, B) Anwendung ist suboptimal, C) Datenbank oder -schema ist nicht akzeptabel. 

Das Problem wird durch die Technik natürlich nicht gelöst - will keiner behaupten - aber scheinbar hat die Anwendung derzeit auch ein weiteres, großes Problem.

gepostet vor 15 Jahre, 2 Monate von HSINC

btw mysql_inerst_id kann man vertrauen, weil es liefert die letzte id der aktuellen connection. insofern kann man eigentlich race conditions ausschliessen. um das aber genau sagen zu können brauchts halt den code + db struktur

gepostet vor 15 Jahre, 2 Monate von knalli

Original von HSINC

btw mysql_inerst_id kann man vertrauen, weil es liefert die letzte id der aktuellen connection. insofern kann man eigentlich race conditions ausschliessen. um das aber genau sagen zu können brauchts halt den code + db struktur

Irgendwo zwischen "kann man vertrauen" und "race conditions ausschließen" gibt es einen kleinen Denkfehler. Und die gibt es praktisch theoretisch quasi so gesehen in der Regel manchmal immer, wenn man wenige große/viele kleine/viele große DML-Anweisungen nicht-transaktional raushaut.

gepostet vor 15 Jahre, 2 Monate von altertoby

Dann mach doch einen (automatisierten) Stress-Test mit all den vorgeschlagenen Log-Möglichkeiten. Damit dürftest du zumindest sehen, ob die Fehler nur von den Anmeldungen alleine ausgelöst werden oder ob noch ein anderer Faktor mit einfließt.

Btw. könnte es nicht sein, dass deine anderen online-User die DB/Ressourcen-Tabelle ständig irgendwie Locken und dann dein Rohstoff-Insert einfach ein TimeOut (oder ein anderen Fehler, kenne mich da nicht so genau aus) bekommt?

gepostet vor 15 Jahre, 2 Monate von D4rk5in

Also ich hab jetzt auch noch mal ein bisschen recherchiert und hab dabei eine interessante Entdeckung auf www.ubuntuusers.de gemacht:

MyISAM-Tabellen

MyISAM ist die Standardtabellenart von MySQL und - sofern nicht explizit anders angegeben - per Voreinstellung genutzt. MyISAM-Tabellen haben grundsätzlich keine Größenbeschränkung und gelten besonders bei häufigen SELECT- und INSERT-Operationen als sehr effizient und schnell. Allerdings sind MyISAM-Tabellen nicht transaktionssicher und unterstüzen keine Fremdschlüssel (sogenannte "Constraints").

InnoDB

Im Gegensatz zu den MyISAM-Tabellen sind InnoDB-Tabellen transaktionssicher und unterstützen Fremdschlüssel. Ist die Datenkonsistenz sehr wichtig, so wird in der Regel InnoDB als Engine eingesetzt.

Mein Fehler könnte eventuell damit zusammenhängen, da die tbl_rohstoffe vom Typ MyISAM ist.

MfG

gepostet vor 15 Jahre, 2 Monate von knalli

Die Anwendung könnte auch mit MyISAM funktionieren. Man kann das auch ohne Transaktionen hinkriegen, wie gesagt.. ich glaube, da steckt mehr als nur der blosse Fehler.

gepostet vor 15 Jahre, 2 Monate von D4rk5in

So, habe nun den Fehler gefunden und ihr werdet lachen... :D

Aber erstmal danke für eure Hilfe! Hab mich dadurch auf jeden Fall einige Stunde mit Datenbanktheorie befasst und so einiges dazulernen können.

Das Problem war im Prinzip nur ein Vergessenes "mysql_query($sql);". Da ich aber schon jahrelang an dem Projekt arbeite und dieses Skript eines der ältesten ist, habe ich doch glatt übersehen, dass ich damals schon einen Algorithmus entwickelt habe, der mir auf der Karte einen freien Platz sucht. Das jedoch nur, wenn der erste Versuch fehlgeschlagen ist. Bei so vielen Usern war nun das Problem, dass die Wahrscheinlichkeit immer größer wurde, dass der erste Versuch freie Koordinaten zu finden, fehlschlägt, da ja immer mehr Plätze belegt werden. D.h. es wurde dann immer der fehlerhafte Algorithmus inkludiert.

So, nun kann ich endlich wieder ruhig schlafen =)

Danke nochmals für eure Mühe!

MfG

Auf diese Diskussion antworten