Daten-Caching im RAM mit Pawn: Optimierung der PlayerInfo-Struktur

Ein technischer Leitfaden zur Erstellung einer performanten RAM-Caching-Architektur mittels PlayerInfo-Arrays in Pawn, um die Festplatten-E/A-Last zu minimieren.

20.05.2026 Deutsch

Optimierung des Daten-Cachings (PlayerInfo) im RAM

Einer der häufigsten Architekturfehler bei der Entwicklung von SA:MP/CRMP-Servern ist das direkte Senden von SQL-Schreibbefehlen bei jeder Statusänderung eines Spielers. Geldvergabe, Waffenbindung, Erfahrungspunkte oder Fraktionsrangänderungen — wenn Ihr Mod auf jedes dieser Ereignisse mit einer schweren UPDATE-Abfrage reagiert, wird der Server schnell zu laggen beginnen.

Bei einer Online-Zahl von 150–200 Spielern steigen die Transaktionen pro Sekunde schnell in die Hunderte. Das Festplatten-Subsystem des Hostings (selbst schnelle NVMe SSDs) wird durch die hohe Anzahl an E/A-Operationen (I/O) völlig überlastet. Das Resultat sind Server-Hänger und drastisch erhöhte Ping-Werte bei den Spielern. In diesem Artikel analysieren wir, wie Sie mithilfe der Struktur PlayerInfo ein effizientes Daten-Caching im Arbeitsspeicher (RAM) einrichten und die Hosting-Last minimieren.

Das Konzept des Cachings: RAM als primäre Quelle der Wahrheit

Der Arbeitsspeicher arbeitet tausende Male schneller als jedes physische Laufwerk. Ein sauberer Ansatz für die Mod-Architektur basiert auf einer einfachen Regel:

Spielerdaten sollten exakt einmal ausgelesen werden — beim Login, und nur beim Verlassen des Spielers oder über einen globalen Autosave-Timer zurückgeschrieben werden.

Die restliche Zeit, in der ein Spieler online ist, sollten sämtliche Manipulationen an Level, Geld oder Inventar ausschließlich im RAM des Servers verarbeitet werden – in einem speziellen Array (dem Cache).

Schritt 1: Variablen richtig strukturieren (Das PlayerInfo-Array)

Zur Erstellung eines Caches in Pawn wird ein Enumerator (enum) mit einem globalen mehrdimensionalen Array kombiniert, dessen Größe fest an die maximale Slotzahl des Servers (Konstante MAX_PLAYERS) gebunden ist.

enum E_PLAYER_DATA
{
    pID,
    pName[MAX_PLAYER_NAME],
    pLevel,
    pMoney,
    pAdminLevel,
    pFraction,
    bool:pIsLoggedIn
}

new PlayerInfo[MAX_PLAYERS][E_PLAYER_DATA];

Beim Verbindungsaufbau sowie beim Verlassen des Servers muss dieser Cache unbedingt geleert werden, damit alte Daten nicht fälschlicherweise auf einen neuen Spieler mit derselben ID übertragen werden:

public OnPlayerDisconnect(playerid, reason)
{
    SavePlayerAccount(playerid);

    PlayerInfo[playerid][pID] = 0;
    PlayerInfo[playerid][pName][0] = EOS;
    PlayerInfo[playerid][pLevel] = 0;
    PlayerInfo[playerid][pMoney] = 0;
    PlayerInfo[playerid][pAdminLevel] = 0;
    PlayerInfo[playerid][pFraction] = 0;
    PlayerInfo[playerid][pIsLoggedIn] = false;
    return 1;
}

Schritt 2: Erstellung der asynchronen Speicherfunktion

Die Speicherfunktion bündelt alle während des Spiels veränderten Parameter aus dem PlayerInfo-Array und sendet sie als Paket per asynchronem mysql_tquery an die Datenbank:

stock SavePlayerAccount(playerid)
{
    if(!PlayerInfo[playerid][pIsLoggedIn]) return 0;

    new query[256];
    format(query, sizeof(query),
        "UPDATE `users` SET `level` = %d, `money` = %d, `admin` = %d, `fraction` = %d WHERE `id` = %d",
        PlayerInfo[playerid][pLevel],
        PlayerInfo[playerid][pMoney],
        PlayerInfo[playerid][pAdminLevel],
        PlayerInfo[playerid][pFraction],
        PlayerInfo[playerid][pID]
    );

    mysql_tquery(db_handle, query);
    return 1;
}

Schritt 3: Einrichtung des globalen Autosaves (alle 15 Minuten)

Um das Projekt im Falle eines plötzlichen Serverabsturzes vor Datenverlusten zu schützen, ist die Einrichtung einer zyklischen automatischen Speicherung notwendig.

Achtung: Speichern Sie niemals alle Spieler in derselben Sekunde! Wenn 300 Personen online sind und ein Timer zeitgleich 300 schwere SQL-Befehle ausführt, wird die Warteschlange blockiert, was zu extremen Verzögerungen führt. Die Speichervorgänge müssen zeitlich gestaffelt werden.

Implementierung eines zeitlich gestaffelten Autosaves:

public OnGameModeInit()
{
    SetTimer("GlobalAutoSave", 60000, true);
    return 1;
}

forward GlobalAutoSave();
public GlobalAutoSave()
{
    static current_chunk = 0;
    
    for(new i = 0; i < MAX_PLAYERS; i++)
    {
        if(!IsPlayerConnected(i) || !PlayerInfo[i][pIsLoggedIn]) continue;
        
        if(i % 15 == current_chunk)
        {
            SavePlayerAccount(i);
        }
    }
    
    current_chunk++;
    if(current_chunk >= 15) current_chunk = 0;
    return 1;
}

Bewertung der Festplattenauslastung

Betrachten wir die mathematische Belastung des Laufwerk-Subsystems:

Script-Architektur Abfrageanzahl (200 Spieler, 1 Stunde Laufzeit) Festplatten-E/A-Last (I/O)
Ohne Caching (UPDATE bei jeder Transaktion, Kill oder Payday-Check) ~ 15.000 — 40.000 Abfragen Kritisch. Verursacht Mikroruckler, Synchronisationsfehler und blockiert Kanäle.
Mit RAM-Caching (Sowie gestaffeltem 15-Minuten-Intervall) ~ 800 — 1.000 Abfragen Minimal. Die Laufwerksauslastung sinkt um 95–98 %. Führt zu absolut flüssigen TPS-Werten.

Fazit: Daten-Caching im RAM ist die einzige professionelle Methode für Server mit hohen Spielerzahlen. Überlassen Sie dem schnellen Arbeitsspeicher die häufigen Zustandsänderungen und nutzen Sie die Datenbank rein als zuverlässiges Langzeitarchiv.

Ähnliche Artikel

Schutz von SA:MP-Servern gegen Flood-Angriffe und gefälschte Pakete in Pawn

Ein technischer Leitfaden zur Absicherung von SA:MP-Servern vor Paket-Flooding, Dialog-Spam und unzulässigem Fahrzeug-Spawning mittels Pawn.RakNet und Nex-AC.

Weiterlesen

Unsichtbare Spieler Bug und Desynchronisation virtueller Welten beheben

Eine technische Analyse des Unsichtbare-Spieler-Glitches sowie der Desynchronisation virtueller Welten in SA:MP/CRMP-Servern inklusive eines sicheren Teleportations-Algorithmus.

Weiterlesen

Bekämpfung von Speicherlecks (Memory Leaks) in AMX-Skripten

Ein technischer Leitfaden zur Erkennung und Behebung von Speicherlecks in SA:MP/CRMP-Server-Skripten. Behandelt MySQL-Cache-Freigabe, Streamer-Entitäten und Heap-Bereinigungen.

Weiterlesen