MySQL R7(+) - Cachefunktionen & Threaded Querys

  • MySQL R7(+) - Cachefunktionen & Threaded Querys

    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:


    [pwn]
    sscanf(store,"p<|>s[24]s[20]s[30]",pName,Fraktion,irgendeinstring)
    [/pwn]
    Wird durch die neuen cache Funktionen zu:
    [pwn]
    cache_get_row(0,0,pName);
    cache_get_row(0,1,Fraktion);
    cache_get_row(0,2,irgendeinstring);
    [/pwn]
    oder zu
    [pwn]
    cache_get_field_content(0,"Name",pName);
    //weiteres hier
    [/pwn]
    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:


    [pwn]
    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
    }
    [/pwn]
    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:
    [pwn]
    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
    [/pwn]
    Wenn ich jetzt z.B einfach ein paar Werte updaten will sieht das ca. so aus
    [pwn]
    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
    [/pwn]

    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:
    [pwn]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
    }
    }[/pwn]

    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:
    [pwn]
    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
    }
    [/pwn]
    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:
    [pwn]
    //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;
    }
    [/pwn]

    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:
    [pwn]
    //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
    [/pwn]

    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:
    [pwn]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
    }[/pwn]

    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:
    [pwn]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
    }[/pwn]

    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:
    [pwn]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
    }[/pwn]

    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 [Blockierte Grafik: http://www.y00u.eu/smileys/awesome-cool.png]


    Mit Freundlichen Grüßen

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von L0g4n () aus folgendem Grund: auf den neusten stand gebracht