RE:NODE

Návody12 min čtení

FiveM oxmysql: nastavení a pomalé dotazy

Jak server FiveM používá databázi: databázový slot v panelu, připojovací řetězec oxmysql, import schématu frameworku, pomalé dotazy a zálohy.

3 čtenáři

Všechno, co si roleplay server FiveM pamatuje mezi restarty, žije v databázi: postavy, peníze, vozidla, nemovitosti, práce, bany. Framework s ní nemluví přímo. Jde to přes oxmysql, resource, která drží pool spojení a zpřístupňuje funkce pro dotazy všem ostatním resource na serveru. Když máš připojovací řetězec správně, už na něj nikdy nemyslíš. Když ne, příznakem není chyba databáze - jsou to hráči, kteří se spawnují bez postavy, peníze, které se po opětovném přihlášení vracejí zpět, nebo celý server zaseknutý na načítací obrazovce, protože jedna resource věčně čeká na dotaz, který nikdy neodpoví.

Co server FiveM vlastně ukládá#

Schéma frameworku je větší, než lidé čekají. ESX Legacy i QBCore dodávají SQL dump, který při importu vytvoří někde mezi patnácti a čtyřiceti tabulkami, a každá resource, kterou přidáš potom, může přidat vlastní.

Ty, na kterých záleží a které nakonec budeš procházet ručně:

  • Tabulka postav. ESX ji nazývá users a klíčuje podle identifier. QBCore a Qbox ji nazývají players a klíčují podle citizenid, s identifikátorem licence hráče v samostatném sloupci. Jeden řádek na postavu.
  • Vozidla. owned_vehicles v ESX, player_vehicles v QBCore, obě klíčované podle poznávací značky a navázané zpět na vlastníka.
  • Práce a hodnosti. V ESX jsou to řádky v databázi v jobs a job_grades. V QBCore a Qboxu je to Lua, ne SQL, což je jeden z praktických rozdílů popsaných v srovnání frameworků FiveM.
  • Bany, logy a cokoli, co se rozhodly vytvořit tvé resource pro telefon, banku a bydlení.

U všech je třeba pochopit jednu věc: zajímavé sloupce jsou bloby JSON. Řádek players v QBCore drží money, charinfo, job, gang, metadata a inventory jako text JSON. ESX dělá totéž s accounts a inventory. Takový návrh drží framework flexibilní a znamená, že uvnitř těch sloupců nejde indexovat, nelze efektivně dotázat „všichni s víc než deseti tisíci v bance“ a při každém SELECT * na hráče se táhnou kilobajty. Také to znamená, že poškozený blob rozbije právě jednu postavu, ne tabulku, což je kompromis, který zvolili.

Postavy se nezapisují průběžně. Oba frameworky ukládají na časovač a při odpojení - qb-core vystavuje interval jako Config.UpdateInterval, v minutách, a ESX má ekvivalent ve své konfiguraci. Cokoli, co ukončí proces bez čistého vypnutí, ztratí až jeden interval postupu pro všechny online. To je nejlepší argument pro restart serveru FiveM tlačítkem Restart v panelu místo jeho zabití.

Databázový slot a co z něj oxmysql potřebuje#

oxmysql mluví protokolem MySQL, takže potřebuje server kompatibilní s MySQL nebo MariaDB. Chce od tebe pět věcí: hostitele, port, uživatele, heslo a název databáze.

V RE:NODE herní tarif zahrnuje jeden databázový slot, vytvořený na záložce Databases v panelu. Panel ti vygeneruje hostitele, uživatele i heslo; databázový server neinstaluješ ani nespravuješ a není tam žádný účet root, o který by ses musel starat. Vedle slotu je tlačítko Open in phpMyAdmin, které tě přihlásí jednorázovým tokenem s platností šedesát sekund - takže procházení tabulek je tlačítko, ne další heslo, které si máš někam uložit. Panel nepojmenovává engine za slotem a oxmysql to po tobě nechce: protokol si domluví při připojení. Důležité je, aby přihlašovací údaje, které ti panel dá, byly ty, které vložíš do server.cfg.

Dvě upřímná omezení. Jeden slot znamená jednu databázi - na framework to stačí, protože ESX i QBCore dávají všechno do jedné, ale nestačí to, pokud bys chtěl v rámci stejného tarifu i samostatnou databázi pro bota na Discordu. A naše řada hostingu databází je PostgreSQL a MongoDB, s žádnou z nich oxmysql mluvit neumí, takže tento produkt není cestou pro upgrade schématu FiveM. Databázový slot v panelu ano.

Připojovací řetězec#

oxmysql čte jeden convar. Dej ho do server.cfg nad řádky ensure, pomocí set a nikdy sets - připojovací řetězec publikovaný do seznamu serverů je během minut vyscrapovaný. Rozdíl mezi těmi třemi je v vysvětlení FiveM server.cfg.

server.cfg
set mysql_connection_string "mysql://s7_user:PaSsW0rd@db.example.net:3306/s7_fivem?charset=utf8mb4"ensure oxmysqlensure ox_libensure qb-core

Části v pořadí: uživatel, heslo, hostitel, port, název databáze, volby. Tři věci se tu pokazí víc než cokoli jiného.

Speciální znaky v hesle. URI má strukturální znaky a @, :, /, ?, # a % v hesle rozbijí parsování způsobem, který vyprodukuje nesmyslnou chybu. Buď je zakóduj procenty (@ se stane %40), nebo použij místo toho podobu se středníky, která je ignoruje:

config
set mysql_connection_string "server=db.example.net;port=3306;userid=s7_user;password=P@ss:word;database=s7_fivem"

Hostitel není `localhost`. Databáze je samostatná služba dosažitelná přes síť, takže 127.0.0.1 se nepřipojí k ničemu. Zkopíruj hostitele přesně tak, jak ho vypisuje panel.

Název databáze má předponu. Databáze vygenerované panelem obvykle nesou předponu serveru jako s7_. Nech ji tam. ER_BAD_DB_ERROR je téměř vždy někdo, kdo ji ořízl.

Přidej ?charset=utf8mb4 a myslí to vážně. Bez toho první hráč, jehož jméno postavy obsahuje diakritiku, cyrilici nebo emoji, zapíše do sloupce rozbité bajty a ty se to dozvíš za týdny. Dva užitečné doplňky: ?connectionLimit=8 omezí pool a oxmysql přijímá obvyklé volby ovladače ve stejném řetězci dotazu.

Při nastavování zapni diagnostiku a jakmile to funguje, vypni ji:

config
set mysql_debug true                 # prints every query, very noisyset mysql_slow_query_warning 150     # milliseconds before a warning

mysql_debug patří jen na testovací server. mysql_slow_query_warning patří zapnutý trvale; je to nejlevnější nástroj na výkon, jaký máš, a pojmenuje zodpovědnou resource.

MySQL.querypool spojeníty, ručněřádkyResourceqb-garagesphpMyAdminjednorázový tokenDatabázový slothost, user, heslooxmysqlpool spojení
Jak se resource dostane k databázi

Import schématu frameworku#

Každý framework dodává SQL dump. Importuj ho před prvním startem, ne poté, co server deset minut vypisuje ER_NO_SUCH_TABLE.

  1. Najdi dump. Bývá v jádrové resource - qb-core/qb-core.sql pro QBCore, ekvivalent ve vydání ESX. Některé buildy ho rozdělují do více souborů a pokud jsou tam cizí klíče, záleží na pořadí.
  2. Otevři phpMyAdmin z panelu, v levém sloupci vyber svou databázi a použij záložku Import.
  3. Při importu nastav znakovou sadu na utf8mb4, aby odpovídala tomu, co jsi dal do připojovacího řetězce.
  4. Potom zkontroluj počet tabulek. Dump, který se zastavil v půlce, zanechá server, který napůl funguje, což je horší než ten, který nestartuje.

Pokud je soubor příliš velký pro limit nahrávání - a dumpy frameworků často jsou - zkomprimuj ho. phpMyAdmin přijímá .sql.gz a .sql.zip a cestou je rozbalí, což typicky zmenší dump osmkrát až devětkrát. Pokud import v půlce vyprší, má stránka Import v phpMyAdminu pole „Skip this number of queries“: spočítej, co už proběhlo, a pokračuj odtud. Průvodce importem a exportem v phpMyAdminu prochází obojí podrobně.

Psaní dotazů, které nebolí#

oxmysql se do resource načte přidáním jednoho řádku do fxmanifest.lua:

lua
server_scripts {    '@oxmysql/lib/MySQL.lua',    'server/main.lua',}

Tím získáš MySQL.query, MySQL.single, MySQL.scalar, MySQL.insert, MySQL.update, MySQL.prepare a MySQL.transaction, každou s variantou .await pro použití uvnitř koroutiny.

lua
-- one row, one valuelocal money = MySQL.scalar.await(    'SELECT money FROM players WHERE citizenid = ?', { citizenid })-- one row as a tablelocal row = MySQL.single.await(    'SELECT charinfo, job FROM players WHERE citizenid = ?', { citizenid })-- many statements, one round trip, all or nothingMySQL.transaction.await({    { 'UPDATE players SET money = ? WHERE citizenid = ?', { newMoney, citizenid } },    { 'INSERT INTO bank_log (citizenid, amount) VALUES (?, ?)', { citizenid, delta } },})

Vždy používej zástupné znaky ? místo skládání hodnot do řetězce. Je to rychlejší, protože server může znovu použít plán, a je to jediná obrana proti hráči, který dá do jména uvozovku a přepíše ti dotaz za tebe.

Čtyři návyky oddělují databázi, která je při 64 hráčích nečinná, od té, která je úzkým hrdlem:

  • Nikdy se neptej v cyklu přes hráče. Jeden dotaz vracející třicet řádků porazí třicet dotazů vracejících jeden. Pokud procházíš hráče a voláš uvnitř cyklu .await, navíc při každé iteraci blokuješ koroutinu.
  • Nikdy se neptej v ticku. Hodnotu ulož do tabulky v Lua a zapisuj ji zpět při změně a při uložení.
  • Vybírej sloupce, které potřebuješ. SELECT * na řádek players přetáhne po drátě každý blob JSON a inventáře nejsou malé.
  • Zápisy seskup do transakce. Deset aktualizací v jedné transakci je jeden round trip a jedno okno zámku; deset samostatných aktualizací je od každého deset.

Pooly spojení a limity pokrývá stranu poolu, která kousne, když se dvacet resource ve stejnou chvíli rozhodne být chytré.

Hledání pomalého dotazu#

S nastaveným mysql_slow_query_warning oxmysql vypíše řádek s názvem resource, spotřebovaným časem a příkazem pokaždé, když dotaz překročí práh. Ten řádek je většinou celé vyšetřování: resource, na kterou se nikdo nepodíval od roku 2022, spouští SELECT s LIKE '%name%' na tabulku, která vyrostla na čtyři sta tisíc řádků.

Když varování nestačí, vezmi příkaz do phpMyAdminu a před něj dej EXPLAIN. Hledáš typ skenu. Řádek, který říká, že dotaz prošel většinu tabulky kvůli jednomu výsledku, je chybějící index a oprava bývá jeden řádek:

sql
ALTER TABLE player_vehicles ADD INDEX idx_citizenid (citizenid);ALTER TABLE owned_vehicles ADD INDEX idx_owner (owner);

Indexuj sloupce, podle kterých filtruješ nebo spojuješ - owner, citizenid, identifier, plate. Neindexuj všechno: každý index je další věc, kterou je třeba zapsat při každém vložení, a tabulka s dvanácti indexy se ukládá pomaleji než ta se třemi.

Druhá polovina problému jsou tabulky, které rostou bez omezení. Logovací resource, zprávy z telefonu, historie transakcí a logy akcí adminů se hromadí navždy, pokud je někdo nepročistí. Měsíční úklid zabrání tomu, aby se z nich stal pomalý dotaz:

sql
DELETE FROM <your log table> WHERE created_at < NOW() - INTERVAL 30 DAY;

Spusť to jednou ručně, zkontroluj počet řádků a pak se rozhodni, zda stojí za to to plánovat. Smazání milionu řádků jedním příkazem drží zámky dlouho, takže u velké tabulky to dělej po dávkách s LIMIT 10000 a opakuj.

Zálohy a věc, na kterou se snadno zapomene#

Záloha serveru bere soubory: resources/, server.cfg, cache. Databáze je samostatná služba na samostatném hostiteli, takže v tom archivu není. Pokud po nějaké potíži obnovíš zálohu serveru, dostaneš zpět každou resource a každou postavu přesně tak, jak je v jakémkoli stavu, v jakém je databáze teď, což může být stav, před kterým jsi utíkal.

Takže exportuj i databázi a ber to jako skutečnou zálohu:

  1. V phpMyAdminu vyber databázi, potom Export, potom Custom.
  2. Formát SQL, výstup komprimovaný gzipem a zaškrtni „Add DROP TABLE“, aby se soubor čistě obnovil na databázi, která už tabulky má.
  3. Stáhni ho a ulož někam, co není herní server. Kopie ležící v resources/ není záloha - zanikne spolu s tím, co chránila.

Dělej to před každou aktualizací frameworku, před spuštěním .sql jakékoli resource a v rutině, kterou skutečně dodržíš. Potom jednou jednu obnov do pomocné databáze a přihlas se - vyzkoušej obnovu dřív, než ji budeš potřebovat existuje proto, že export, který nikdy nebyl importován, je hypotéza. Zálohy a obnova databází má obecný postup a návody k panelu uvádějí, kde slot najdeš.

Chyby připojení a co znamenají#

ChybaPříčina
ECONNREFUSEDŠpatný hostitel nebo port, nebo localhost v řetězci
ER_ACCESS_DENIED_ERRORŠpatný uživatel nebo heslo, nebo nezakódovaný znak
ER_BAD_DB_ERRORŠpatný název databáze, obvykle ořízlá předpona
ER_NO_SUCH_TABLESchéma se nikdy neimportovalo, nebo se importovalo jinam
ER_BAD_FIELD_ERRORResource čeká sloupec, který tvé schéma ve své verzi nemá
ER_DATA_TOO_LONGBlob JSON přerostl typ svého sloupce
ER_CON_COUNT_ERRORPříliš mnoho spojení; sniž connectionLimit

Ještě dvě věci, které nejsou chyby, ale vypadají tak. Connection lost: the server closed the connection na nečinném serveru je databáze, která zahodila zastaralé spojení, a oxmysql ho znovu otevře - pokud se to stane jednou za hodinu a nic se nerozbije, ignoruj to. A první dotaz, který po restartu trvá dvě sekundy, je zahřívání poolu, ne pomalá databáze.

Pokud se nepřipojí vůbec nic, zkontroluj v tomto pořadí: convar je set a správně napsaný, ensure oxmysql stojí před vším, co ho používá, hostitel je hostitel z panelu a ne localhost a heslo neobsahuje nezakódované speciální znaky. Tato posloupnost vyřeší téměř každý případ.

FAQ#

Potřebuje server FiveM databázi?

Jen pokud si má cokoli pamatovat. Freeroamový nebo závodní server bez perzistence běží bez databáze úplně v pořádku. Každý roleplay framework ji potřebuje, protože postava, která nepřežije restart, není postava.

Můžu místo toho použít PostgreSQL nebo MongoDB?

Ne s oxmysql ani s žádným běžným frameworkem FiveM. Jsou napsané proti protokolu MySQL a jeho dialektu SQL. Naše řady PostgreSQL a MongoDB jsou pro aplikace, ne pro ESX nebo QBCore.

Jak velká databáze FiveM dorůstá?

Menší, než se lidé obávají. Server s několika tisíci postavami, jejich vozidly a rokem logů má obvykle nízké stovky megabajtů. Rostou logovací tabulky, ne postavy - pokud ta tvoje měří gigabajty, zodpovídá za to jedna logovací resource a její pročištění většinu vrátí.

Proč všichni po pádu přišli o peníze?

Protože postavy se zapisují na časovač, ne při každé změně. Proces, který skončí bez čistého vypnutí, ztratí všechno, co se stalo od posledního uložení, u každého online hráče. Zkrať interval ukládání frameworku, pokud ztráta znamená víc než zátěž zápisů.

Můžou dva servery sdílet jednu databázi?

Technicky ano a tak lidé provozují testovací server nad skutečnými daty. Nedělej to se dvěma ostrými servery na stejném frameworku: oba budou cachovat a zapisovat stejné řádky postav a vyhrává ten, kdo zapíše naposled, což hráči zažijí jako mizející předměty.

Musím umět SQL, abych provozoval server FiveM?

Stačí tolik, abys přečetl EXPLAIN, přidal index a exportoval dump. Zbytek udělá dump samotného frameworku. Všechno v tomto článku jsou čtyři nebo pět příkazů, které můžeš zkopírovat, a většinu z nich ti napíše phpMyAdmin.


Komentáře

Zcela anonymně: žádný účet, žádný e-mail, žádné cookie. Ukládáme jen jméno, které napíšeš, text a čas - nic dalšího. Počet odkazů je omezený a značkování se nevykresluje.

0/2000