• Es freut uns dass du in unser Minecraft Forum gefunden hast. Hier kannst du mit über 130.000 Minecraft Fans über Minecraft diskutieren, Fragen stellen und anderen helfen. In diesem Minecraft Forum kannst du auch nach Teammitgliedern, Administratoren, Moderatoren , Supporter oder Sponsoren suchen. Gerne kannst du im Offtopic Bereich unseres Minecraft Forums auch über nicht Minecraft spezifische Themen reden. Wir hoffen dir gefällt es in unserem Minecraft Forum!

MySQL Settings und Arrays

CubBossa

Schafhirte
Registriert
17 Juli 2015
Beiträge
125
Diamanten
369
Minecraft
CubBossa
Hello again. Ich bins mal wieder und ich hab mal wieder eine Frage zu MySQL.

Ich habe ein Setting System für meinen Server mit MySQL gebaut. Ich habe alles, was ich hier gelernt habe angewand und umgesetzt und Datenbankzugriffe laufen asynchron und threadsafe, habe auf Hikari umgestellt für Pooling, und ich hab das Gefühl, dass es ganz gut läuft.
Jetzt wollte ich spontan einstellen (da die Spielerzahlen steigen), dass man für sich die Joinmessage aktivieren oder deaktivieren kann.
Mein Ansatz: Spieler joint, ich stumm die join message und schreibe jedem, der in seinen Settings eingestellt hat, dass die Joinmessage sichtbar sein soll, manuell die Joinnachricht. Per p.sendMessage...

Jetzt war mein erster Gedanke: Ist es nicht eine Katastrophe, bei zb 20 Online-Spielern bei jedem join und quit (man denkt zb an Internetprobleme bei einem Spieler) für alle 19 anderen Spieler dann wieder in die MySQL zu gucken?
Oder hab ich jetzt viel zu viel Angst, mit MySQL Performanceprobleme zu beschwören und eigentlich würde das schon gehen. Löst Hiraki durch Pooling vllt eh alle Probleme?
Oder sollte ich zb eine Query verwenden, die gleich für alle Online Spieler abfragt.
Also dass meine Methode lautet:
getSetting(UUID[] players, Setting[] settings, Callback callback)
statt für jeden Spieler einen einzelnen zugriff zu machen?
Bisher: getSetting(UUID player, Setting setting, Callback callback) und getSetting(UUID player, Setting[] setting, Callback callback)

Sollte ich vielleicht diese spezielle Setting beim Join in ein Array cashen? Das Problem ist, dass man die Setting von allen Servern aus ändern kann und der Wert also nach dem Ändern auf einem anderen Server nicht mehr mit dem im Array übereinstimmt.

Mir fehlen da die Erfahrungswerte, weil ich ja bisschen selbststudium betreibe und auch gar nicht richtig weiß, wie ich die Performance messe und ab wann es kritisch wird.
Hier im Forum hab ich immer ziemlich aufschlussreiche Antworten bekommen, daher dachte ich, ich frag mal :)

Liebe Grüße, CubBossa
 

Chrisliebär❤️

nur echt mit ❤️
Moderator
Registriert
19 Mai 2014
Beiträge
1.675
Diamanten
830
Sollte ich vielleicht diese spezielle Setting beim Join in ein Array cashen? Das Problem ist, dass man die Setting von allen Servern aus ändern kann und der Wert also nach dem Ändern auf einem anderen Server nicht mehr mit dem im Array übereinstimmt.
Das ist exakt was du tun solltest und beschreibt auch wunderbar, warum ich grundsätzlich der Meinung bin, dass eine Datenbank ein Komplexitätsmonster ist, das überhaupt nicht erstrebenswert sein sollte.

Wenn du viele Spielerdaten hast und der Spieler den Server betritt ist es das schlauste die Daten dann sofort in den RAM zu laden. Leider ist bei Bukkit meines Wissens auch das Loginevent synchron, wobei man mit ProtoclLib darum arbeiten könnte, indem man das Paket abfängt und später in den Socket injiziert.

Wenn du mehrere Server hast, die Caching bereiben, brauchst du Cachekoheränz. Das Wort findet man vor allem in Kontext von Mehrkern CPUs, aber das Problem ist das selbe: Du musst anderen Teilnehmern mitteilen können, wenn sie den Cache updaten müssen. Der naive Ansatz ist, dass du den Cache regelmäßig neu aufbaust. Wenn das aufgrund der Verzögerung nicht möglich ist, müssen alle Cachenutzer informiert werden. Wie man das am besten macht hängt vom Anwendungsfall ab, da man aber unter Bukkit immer an den Main Thread gebunden ist, ist das tatsächlich ziemlich kompliziert.

Falls es nur Spielerkonfigurationen sind, kann der Spieler beispielsweise eh nur auf einem Server sein, das heißt eventuell reicht eine einfache Nachricht an den aktiven Server, dass er die Daten neuladen muss.

Verteilte Datenkonsistenz ist aber ein sehr komplexes Problem, dass man am Besten anwendungsspezifisch löst. Eine allgemeine Lösung ist mir nicht bekannt und ich müsste selbst überlegen wie ich das lösen würde. Mein erster Ansatz wär vermutlich ein zentraler Konfigurationsdienst, der alle Daten im RAM hält und die einzelnen Server über Netzwerk versorgt, aber selbst das würde den Main Thread blockieren.
 

EinDev

Minecrafter
Registriert
22 März 2016
Beiträge
23
Diamanten
112
Ich kann dazu nichts ergänzen, nur noch folgende Idee:
Du könntest die Joinmessage erst senden, nachdem du die SQL-Abfrage gemacht hast. Dann kommt ggf. die Joinmessage erst ein paar ms später, ich gehe aber nicht davon aus dass man es beim Joinen merken wird.
Wichtig ist nur, dass du die SQL-Abfrage trotzdem asynchron machst.
 

CubBossa

Schafhirte
Registriert
17 Juli 2015
Beiträge
125
Diamanten
369
Minecraft
CubBossa
Okay mein Schädel brummt :D dass Datenbanken ein komplexes Ding sind, seh ich gern ein und ich würde auch wenns geht gerne komplett drauf verzichten. Ich habe mich nur längst entschieden einen Server zu leiten und jetzt finde ich es viel zu aufregend, meine Vorstellungen nicht in die Tat umzusetzen, auch wenn ich dadurch viel neu lernen muss und meine Spieler manchmal geduldig sein müssen. Das sind sie :D

Die Synchronität würde ich umgehen, wie du, EinDev, auch geschrieben hast. Also einfach im Callback dann die Message senden. Das merkt ja keiner, dass sie verzögert ist. Also die Originalnachricht cancellen und dann via p.sendMessage eben eine nachricht schicken.

Leider bin ich nicht so deep in der Informatik, damit ich bei dem was du, Chrisliebär, beschreibst mitkomme :D Also das Thema grundsätzlich finde ich trotzdem spannend und über meine Serverplanung hinaus les ich mich da vielleicht mal rein, mit den Ansätzen, die du mir gegeben hast. Aber weil es dank meiner Ahnungslosigkeit nicht ganz im Verhältnis steht zu dem, was ich vorhabe, richte ich mir glaube ich erstmal einfach die Abfrage mit den mehreren UUIDs und mehreren Settings ein. um nur einen Datenbank zugriff pro join zu haben.

Also folgendermaßen:
- Spieler joint: Joinmessage wird gecancelled.
- Datenbankabfrage mit einem Statement, das nach Setting key und Setting Value für "Nachricht anzeigen" filtert und ein Array aus UUIDS bereitstellt.
- Im Callback asynchron: versenden der Join message an alle Spieler aus dem Result Array

Das sollte ja dann recht performant sein, weil man pro join und quit nur eine Datenbank abfrage hat und der Server ja nicht freezed sondern nur der Verkehr mit der Datenbank erhöht wird. Das einzige Problem, das ich mir vorstellen könnte ist, dass je nach dem wie viele Spieler schon gespielt haben der Result Array recht groß werden könnte. Weil mysql ja nicht weiß, welcher Spieler online ist.
 

CubBossa

Schafhirte
Registriert
17 Juli 2015
Beiträge
125
Diamanten
369
Minecraft
CubBossa
Aber dann muss ich ja für jeden Onlinespieler ein Statement schreiben. Ich dachte, das wäre das größere Performanceproblem, dass ich bei jedem join und quit gleich 15 20 mal was von meiner Datenbank will, statt sie nur einmal anzufragen und mit dem ergebnis zu arbeiten. Oder seh ich das falsch
 

EinDev

Minecrafter
Registriert
22 März 2016
Beiträge
23
Diamanten
112
Ein Performanceproblem ist es nur, wenn die SQL-Abfrage den Hauptthread blockiert. Wenn du die Abfrage asynchron machst, sollte das kein Problem sein.
Grundsätzlich brauchen SQL-Abfragen kaum Rechenleistung, sie können nur einen Thread relativ lange blockieren während sie auf die Antwort vom Server warten.
 

Chrisliebär❤️

nur echt mit ❤️
Moderator
Registriert
19 Mai 2014
Beiträge
1.675
Diamanten
830
Ich kann dazu nichts ergänzen, nur noch folgende Idee:
Du könntest die Joinmessage erst senden, nachdem du die SQL-Abfrage gemacht hast. Dann kommt ggf. die Joinmessage erst ein paar ms später, ich gehe aber nicht davon aus dass man es beim Joinen merken wird.
Wichtig ist nur, dass du die SQL-Abfrage trotzdem asynchron machst.
Ja das ist grundsätzlich der beste Ansatz, wird aber richtig komplex, wenn man z.B. an Schadensberechnungen oder ähnliches denkt. Die Aktion solange zu verzögern, bis der Datensatz eingetroffen ist. Da wir halt in einem Spiel mit Echtzeitanforderungen sind, ist das kompliziert.
 

BloodEko

Schafhirte
Showcase Teilnehmer
Registriert
9 September 2012
Beiträge
144
Diamanten
36
Eine einfache Möglichkeit um auf synchrone Events reagieren zu können, ist die Settings im
Ram zu halten. Heißt, du hast sie bereits vorher geladen, oder reagierst später via Callback.

-> wenn der Spieler joint, werden seine Settings async in ein Cache geladen.
-> wenn die Settings bereits im Cache sind, kann der DB call gespart werden.
-> in regelmäßigen Abständen wird der Cache mit der DB synchronisiert.
-> via das Cache iterierst du nur über relevante und keine offline Spieler.
-> falls Settings noch nicht geladen sind, wird die Aktion via Callback später ausgeführt.
-> falls eine Verzögerung nicht möglich ist (andere Events) wird die Aktion abgebrochen.

Wenn also jetzt Spieler A joint, wird die Nachricht gecancelt. Dann gehst du mit einer Schleife
über die online Spieler und holst ihre Spielerdaten aus einer Map. Sind sie vorhanden, kannst
du direkt die Nachricht senden. Sind sie null, musst du ein Callback beim Cache registrieren,
welches synchron aufgerufen wird, nachdem die Daten für Spieler B geladen wurden.

Allerdings du siehst, es ist nicht ganz einfach. Je nach Event muss es wieder überdacht werden,
da es neue Situationen gibt. Z.b. wäre ein Spieler unverwundbar, bis seine Daten geladen sind.
Bei einem Shopmenü hingegen reicht es nur das Öffnen solange abzubrechen.
 
Zuletzt bearbeitet:

CubBossa

Schafhirte
Registriert
17 Juli 2015
Beiträge
125
Diamanten
369
Minecraft
CubBossa
Okay das hört sich ziemlich klug an. Danke für die ausführliche Erklärung :D
Jetzt ist es so, dass der Spieler die Einstellung ja von allen Bungeecord-Unterservern ändern kann. Heißt, er könnte es ändern via Command und dann wird der Spieler ja trotzdem Join- und Quitnachrichten sehen, bis sein Cache automatisch refreshed wurde oder?

Mein Vater hatte noch einen sehr guten Einwand den ich mal in den Raum werfen wollte. Ich kann ja aus der Datenbank den letzten Login des Spielers auslesen und dann ein Statement schreiben, welches zb sagt (äh mal nur grob ungestetet von der theorie)

SELECT uuid FROM test WHERE setting_key = show_login AND setting_value = 1 AND anderer_table.last_login > anderer_table.last_logout;
Annahme, dass die login und logout als longs gespeichert sind.
Weil damit hab ich nur 1 Query, asynchrone Reaktion, kann mir das Cache Problem sparen, bei dem ich über die Serverchannel kommunizieren müsste und würde einen Array aus UUIDs, die alle online sind und eine Message bekommen sollen, als result haben.

Wie findet ihr diesen Ansatz?
 

BloodEko

Schafhirte
Showcase Teilnehmer
Registriert
9 September 2012
Beiträge
144
Diamanten
36
Die Daten werden aufgebaut im PreLoginEvent. Danach gehen alle Änderungen über den Cache und sind sofort aktiv.
Mehrere Server machen es natürlich schwieriger, da du die Settings zuerst den anderen Server schicken musst.

Wenn du immer direkt ausliest, kannst du theoretisch auf das Cachen verzichten. So erfolgt die Übertragung
zwischen den Servern automatisch über die Datenbank. Nachteil davon ist, dass du bei den Events nur via
Callback reagieren kannst und pro Zugriff jedes mal eine Datenbankabfrage benötigt wird.
 
Zuletzt bearbeitet:
Oben