MySQL Tutorial - Cachefunktionen & Threaded Querys
Hallo Leute,
und herzlich willkommen zu diesem Tutorial
Wie man sicher schon an der Überschrift geht es heute um die R7 des MySQL PLugins.
Wichtig: Dieses Tutorial bezieht sich auf die R7 Version des MySQL Plugins von BlueG.
(Logisch dass von Strickenkid und ältere Versionen des Plugins werden hier mit dem vorkommenden Code nicht klappen)
Entweder ihr downloadet es hier oder ihr braucht es gar nicht zu downloaden, da seit dem "Server Pack" der Version 0.3e ist sie standardmäßig bei
den Includes dabei, wenn ihr euch den SAMP Server von sa-mp.com herunterladet.
Die Include war beim 0.3e Server Packet dabei, ist aber inzwischen nicht mehr standardmäßig mit drinnen.
Hier die Google Project Seite für das MySQL Plugin von BlueG: Klick mich!
Wichtig: Hier wird nur erklärt, was sich bei der R7 Version geändert hat und wie man jetzt Daten ausließt, wie man eine MySQL Verbindung herstellt, wird
in Maddin's Tutorial ausführlich erklärt.
Aber nun fangen wir doch einfach mal an.
Inhaltsverzeichnis:
1. Wichtigsten Änderungen der R7 Version im Überblick
2. Was sind Threaded Querys überhaupt und was sind die Vorteile ?
3. Vorteile der Cachefunktionen
4. Erklärung aller wichtigen neuen Funktionen
5. Schlusswort
1. Änderungen der R7'er Version im Überblick
Mit dem Update auf R7 des MySQL Plugins von BlueG konnten sich noch nicht alle anfreunden.
Grund dafür war, dass es viele Funktionen mittlerweile nicht mehr gibt bzw. Sie einen anderen Namen bekommen haben oder ihre
Syntax sich verändert hat, die im ersten Moment für Verwirrung sorgt. Dazu kommt, dass mittlerweile nur noch threaded Querys unterstützt werden, was das zu bedeuten hat, wird in Kapitel 2 näher erläutert.
Desweiteren wurden Cache Funktionen hinzugefügt, die das Auslesen von Daten aus einer Tabelle einfacher machen sollten.
Dabei braucht man nicht mehr mit mysql_free_result oder mysql_store_result anzuwenden um das Ergebnis zu speichern oder nach der Beendigung des Querys das Ergebnis bzw. den Speicher wiederfreizugeben.
2.Was sind threaded Querys überhaupt und was sind die Vorteile ?
Kommen wir zu Beantwortung der Frage, was threaded Querys überhaupt sind.
Ich glaube zu diesem Thema gab es auch schon mal ein Tutorial, das sich jedoch auf das MySQL Plugin von Strickenkid bezog ( Das findet ihr hier ) (bei BlueG's sieht das nur ein bisschen anders aus). Wenn man mit MySQL Sachen auslesen will, bzw. einen Query ausführt, benötigt es eine gewisse Verarbeitungszeit, um diesen fertigzustellen. Wenn man jetzt z.B 3000 Autos auslesen will, bzw. man generell eine große Rate an Anfragen hat, wartet PAWN auf die Fertigstellung des Querys, da eben nicht 2 Dinge auf einmal gemacht werden können, was wiederum im schlimmsten Falle zu Laggs führen könnte.
Das kann eben passieren, wenn man beim Ausgeben eines Querys einfach im Code weiterschreibt und nicht einen 2.Thread eröffnet, wo der Code nach dem Query weitergeht. Wenn man jetzt aber einen 2.Thread eröffnet ( bzw. Callback), in dem der Code weiter geht nach dem Beendet des Querys, können in der Zwischenzeit noch andere Dinge von PAWN erledigt werden. So werden Laggs und im schlimmsten Fall sogar ein Serverabsturz vermieden.
Wie gesagt, hier nochmal der Verweiß auf das Tutorial OnQueryFinish, wo das nochmals ausführlich erklärt wird: Klick mich!
3. Vorteile der Cachefunktionen
Einzig und allein reichen threaded Querys aber dazu nicht aus, um die Geschwindigkeit um einiges zu verbessern, da man bisher immer noch
mysql_store_result() bzw. mysql_free_result() verwenden musste, um Daten richtig auslesen zu können.
Vorteile:
- Mithilfe der Cachefunktionen wird unser gesamter Code schneller. Die Geschwindigkeit verbessert sich um das 10-20 fache ( Klick mich für das Ergebnis )
- Man braucht nicht mehr mysql_store_result() bzw. mysql_free_result() zu verwenden
- Es ist mittlerweile auch leichter Daten effektiv auszulesen als mit sscanf ( und auch schneller als sscanf ! )
Hier mal ein Beispiel wie man mit sscanf Daten ausgelesen hat:
sscanf(store,"p<|>s[24]s[20]s[30]",pName,Fraktion,irgendeinstring)
Wird durch die neuen cache Funktionen zu:
cache_get_row(0,0,pName);
cache_get_row(0,1,Fraktion);
cache_get_row(0,2,irgendeinstring);
oder zu
cache_get_field_content(0,"Name",pName);
//weiteres hier
Das ganze hier ist schneller als sscanf & auch einfacher für die, die spezifischen Symbole davon nicht kennen.. Zwar ist das parsen der einen Linie von sscanf schneller als die cach_get_rows bzw.
cache_get_field_content, jedoch arbeitet die sscanf Lösung auch mit mysql_fetch_row, was einen extra Array benötigt die "Daten" zu holen und dann
daraus zu parsen. Beim Caching ist das jedoch schneller.
Die genaue Syntax der neuen Funktion bzw. Erklärung folgt in Kapitel 4.
4. Erklärung aller neuen Funktionen
In diesem Kapitel werde ich euch die Syntax,Nutzen und jeweils ein Beispiel der neuen Funktionen bringen.
mysql_function_query(connectionHandle,query[],bool:cache,callback[],format[],{Float,_}:...)
Fangen wir doch mal beim wichtigsten an, um einen Query nutzen zu können.
Die Funktion mysql_query wurde durch mysql_function_query ersetzt. Dabei wurden auch noch neue Parameter bei der letzeren Funktionen hinzugefügt.
Die Erklärung der Parameter
- connectionHandle = Logischerweiße die ConnectionHandle eurer MySQL Verbindung. Speichert den Rückgabewert von mysql_connect am besten in einer globalen Variable, und setzt diese dann hier immer ein.
-query[] = Der Query der ausgeführt werden soll. z.B "SELECT XX FROM XX Where YY = XX"
-bool:cache = Setzt ihr dies auf true, wird festgelegt, dass ihr den Cache verwenden wollt, false, deaktiviert den Cache. Empfehlenswert ist es, den Cache nur bei SELECT Abfragen zu nutzen, bei UPDATE INSERT, etc. lasst ihr das am besten auf false
-callback[] = Der Thread bzw. das Callback, in dem der Code weiterlaufen soll. Auch hier, am besten nur bei SELECT Abfragen verwenden, bei allen
anderen Abfragen einfach leerlassen also "" einfach reinschreiben
-format[] = Hier habt ihr die Möglichkeit, Parameter in den jeweiligen Callback (wenn ihr einen benutzt) mitzuliefern, wie z.B Bei SetTimerEx
-{Float }: ... = Hier kommt rein, was ihr für Parameter an das entsprechende Callback übergeben wollt.
Ein kleines Beispiel zu mysql_function_query:
mysql_function_query(handle,"SELECT ... FROM table ...'",true,"QueryFinished","si","Logan_Adams",playerid); //Führen einen SELECT Query aus, das kommt ins Callback QueryFinished, nutzen den Cache und übergeben Parameter an das Callback um eine Erstellung von anderen Variablen zu ersparen
forward QueryFinished(Name[],playerid); // Wir forwarden unseren Callback mit den entsprechenden, oben angegebenen Parametern
public QueryFinished(Name[],playerid) { //Wir erstellen unseren Callback
printf("Name: %s , ID: %d",Name,playerid); // Geben es in der Konsole aus
}
Wie gesagt, das obige ist nur ein Beispiel
Nochmal erläutere ich das richtige Einsetzen des Caches ( macht nur bei SELECT Abfragen wirklich Sinn ( die ein Ergebnis zurückliefern) )
Ein paar kleine Beispiele:
mysql_function_query(handle,"SELECT * From accounts",true,"LoadPlayers",""); //Wir führen einen SELECT Befehl aus, aktivieren den Cache, lassen es ins Callback "LoadPlayers" umleiten
Wenn ich jetzt z.B einfach ein paar Werte updaten will sieht das ca. so aus
mysql_function_query(handle,"UPDATE accounts SET level = 10 WHERE Name = 'Logan_Adams'",false,"",""); //Hier führen wir einen simplen Update Query aus, deaktivieren den Cache und lassen in kein Callback umleiten
cache_get_data(&num_rows,&num_fields,connectionHandle=1)
Diese Funktion returnt bzw. gibt die Anzahl der Zeilen und Felder zurück, die der Query returnt.
- num rows = Anzahl der Zeilen
- num fields = Anzahl der Felder
Man kann diese Funktion z.B dazu verwenden, um zu überprüfen, ob der Spieler schon auf dem Server registriert ist
Ein Beispiel:
new query[128];
format(query,sizeof query,"SELECT * FROM accounts WHERE Name = '%s'",SpielerName(playerid));
mysql_function_query(dbhandle,query,true,"OnPlayerCheck","d",playerid); //Ausgeben aller Daten vom jeweiligen Spieler
forward OnPlayerCheck(playerid); //Forwarden unseres Callbacks
public OnPlayerCheck(playerid) {
new zeilen,felder; //Erstellen zweier Variabeln für die Zeilen und Felder
cache_get_data(zeilen,felder); //Benutzen unsere Funktion um die Zeilen und Felder zu erhalten die oben im Query angefragt wurden
if(!zeilen) { //Wenn es keine Zeilen gibt , d.h. der Spieler noch nicht registriert ist
//Weiter Code ....
}
else { //Wenn es doch welche gibt, d.h. der Spieler ist schon registriert
//Weiter Code hier
}
}
cache_get_row(row,idx,dest[],connectionHandle=1)
Diese Funktion ist eine, die man eigentlich am meisten benutzt. Damit können wir viele unterschiedliche Daten auslesen.
Diese Funktion nimmt den zeilen und den Feld Index und speichert dessen Daten im angegebenen String.
- row = Zeilen Index meistens 0
- idx = Felder Index
- dest[] = Der String, in dem wir die ausgelesenen Daten zwischenspeichern wollen
Wie gesagt wir brauchen den Felder Index. Wenn unsere Tabelle, beispielsweiße so aufgebaut ist:
ID ( Auto Increment ) = Feld Index 0
Name = Feld Index 1
Passwort = Feld Index 2
Level = Feld Index 3
...
Hier bei fällt auf, dass der Feld Index immer bei 0 beginnt, was man sich merken muss (ist aber bei switch&case auch so)
Wenn ihr also so Daten auslesen wollt, immer auf den richtigen Index achten ( ein Blick in eure MySQL Tabelle genügt aber dafür, um diesen herauszufinden)
Ein kleines Beispiel:
mysql_function_query(..........) //Unser Query
forward ...... //Forwarden
public ...... { //Das entsprechende Callback
new speicher[30]; //Erstellen unserer Variable um die Daten zwischenzuspeichern
cache_get_row(0,0,speicher); //Holt uns die Daten von jetzt z.B 'ID'
printf("Seine ID: %d",strval(speicher)); //Konvertieren in eine Zahl, um es ausgeben zu können
cache_get_row(0,1,speicher); //holen uns daten aus z.B "Name"
printf("Name: %s",speicher); //Ausgeben
cache_get_row(0,2,speicher); //Holen uns Daten aus "Passwort"
printf("Passwort: %s",speicher); //Ausgeben in der Konsole
cache_get_row(0,3,speicher); //Holen uns Daten aus "Level". Da dies aber eine Zahl ist, müssen wir diese noch konvertieren
printf("Level: %d",strval(speicher)); //Ausgeben mit Konvertieren des Strings
}
Wichtig: Das was ihr erhaltet ist immer ein String. Wenn ihr jetzt z.B Level mit "speicher" ausgelesen habt müsst ihr z.B eurem Spieler Enum
SpielerInfo[playerid][Level] "speicher" zuweisen, aber nicht vergessen, speicher in eine Zahl mit strval zu konvertieren.
Bei Floats, dann natürlicht mit floatstr ...
cache_get_field(field_idx,dest[],connectionHandle=1)
Diese Funktion speichert den Namen eines Feldes.
- field_idx = Der Feld Index des Felder, der ausgelesen werden soll
- dest[] = Der String, indem der Name des Feldes zwischengespeichert werden soll
Ein kleines Beispiel dazu wäre:
//Den oberen Teil lasse ich weg ....
public QueryBeendet()
{
new fname[30]; //Erstellen einer Variablen
cache_get_field(0,fname); //Holen uns den Namen des Feldes mit dem Index 0 in die Variable "fname"
printf("Name des Feldes mit dem Index 0: %s",fname); //Ausgeben in der Konsole
return 1;
}
cache_get_field_content(row,const field_name[],dest[],connectionHandle=1)
Diese Funktion macht eigentlich genau das gleiche wie cache_get_row. Der Unterschied hierbei ist nur, dass ich keinen Feldindex angeben muss.
Für Leute, die zu faul sind, sich nach dem diesem zu erkunden, ist diese Funktion das richtige. Der
Geschwindigkeitsunterschied ist auch nur ziemlich gering.
- row = Die Zeile die ich auslesen will ( meistens 0)
- const field_name[] = Der Name des Feldes das ich auslesen will. z.B "Passwort"
- dest [] = String, in denen ich die Daten zwischenspeichern will
Ein Beispiel dazu:
//Oberer Teil ist wieder weggelassen
//Natürlich muss unten stehender Code in ein Callback
new store[24];
cache_get_field_content(0,"Name",store); //Holen uns Daten aus "Name" und speichern in "store"
printf("Name: %s",store); //Ausgeben des Wertes in der Konsole
cache_get_row_int(row,idx,connectionHandle=1)
Achtung: Diese Funktion wird nur bei den Versionen R8+ unterstützt.
Diese Funktion ist eigentlich nur eine Variation der Funktion cache_get_row, die Syntax ist auch fast die gleiche, außer,
dass es keinen destination Parameter gibt. Stattdessen wird das über die Rückgabe geregelt.
Mit dieser Funktion ist es gleich möglich Spalten in einen Integer Wert zu speichern, man muss also nicht mehr mit strval den String
von cache_get_row in eine Ganzzahl konvertieren.
Ein kleines Beispiel dazu:
public ...... { //Das entsprechende Callback
new speicher;//Variable um die Ganzzahl zu speichern
speicher = cache_get_row_int(0,0); //Holt uns die Daten von jetzt z.B 'ID' (natürlich muss der Index wieder stimmen)
printf("Seine ID: %d",speicher); //Ausgabe, aber dieses Mal ist keine Konvertierung nötig
}
Neue Tests haben ergeben, dass der neue Code hier im Beispiel ca. 1,5 mal schneller ist, als der alte Code mit Konvertierung.
cache_get_row_float(row,idx,connectionHandle=1)
Achtung: Diese Funktion wird nur bei den Versionen R8+ unterstützt.
Wieder "nur" eine Variation der Funktion cache_get_row, funktioniert wie cache_get_row_int.
Auch hier kann man einen Float Wert von einer Spalte auslesen und auch wieder in einen Float-Wert speichern, ohne Konvertierung.
Beispiel zu dieser neuen Funktion:
public ...... { //Das entsprechende Callback
new Float:speicher;//Variable um die Gleitkommazahl zu speichern
speicher = cache_get_row_float(0,5); //Holt uns die Daten von jetzt z.B 'LastX' (natürlich muss der Index wieder stimmen)
printf("LastX-Koordinate: %f",speicher); //Ausgabe, aber dieses Mal ist keine Konvertierung nötig
}
Auch hier ist der neue Code ca. 2 mal schneller als mit Konvertierung.
cache_get_field_content_int(row, const field_name[], connectionHandle = 1);
Achtung: Diese Funktion wird nur bei den Versionen R8+ unterstützt.
Diese Funktion bewirkt eigentlich genau das gleiche wie cache_get_row_int, nur dass man hier nicht den Zeilen Index angeben muss,
sondern nur den Namen des zu Inhalt holenden Feldes, konvertiert dabei dann das Ergebnis gleich in eine Ganzzahl, um nicht
selbst manuell konvertieren zu müssen.
Beispiel:
public .... { //das entsprechende Callback
new store;
store = cache_get_field_content_int(0,"ID",dbhandle); //Inhalt aus dem Feld ID holen
printf("ID: %d",store); //In der Konsole ausgeben
}
cache_get_field_content_float(row, const field_name[], connectionHandle = 1);
Achtung: Diese Funktion wird nur bei den Versionen R8+ unterstützt.
Macht genau das gleiche wie cache_get_field_content_int, konvertiert das Ergebnis jedoch jetzt gleich in eine Gleit bzw. Fließkommazahl.
Beispiel ist hier, so denke ich, nicht von Nöten.
5. Schlusswort
Ich hoffe ich konnte mit diesem Tutorial einigen Leuten helfen, die bisher mit der R7 Version noch nicht zurecht gekommen sind.
Wenn irgendwo Fehler im Tutorial sind, bitte macht mich darauf aufmerksam
Mit Freundlichen Grüßen