Kitle: FiveM sunucu sahipleri, betik yazarları, bakımcılar
Amaç: Yer değiştirmek mysql-async ile oxmysql güvenli bir şekilde, sorguları hızlandırın ve SQL kullanımınızı modernleştirin.
Ayrıca şunu da okuyun:
- FiveM Sunucu Optimizasyonu: 2025'in Kesin Oyun Kitabı — https://fivemx.com/how-to-optimize-fivem-server-performance/
- Adaptör Desenleri: ESX↔QBCore↔QBOX Dışa Aktarımlar, Olaylar ve Oyuncu Modelleri — https://fivemx.com/adapter-patterns/
Özetle;
- Kullanmak
oxmysql: hazırlanmış ifadeler, promise\/await API, daha iyi tanılama, güçlü performans. - Minimum kod değişiklikleri: değiştir
@param→?(konumsal) veya:name(adlandırılmış) parametreler; değiştirinMySQL.Async.*çağrıları ileMySQL.*/exports.oxmysql:*. - SQL “UP” betiklerini çalıştırın aşağıda (karakter seti\/indeks düzeltmeleri) ve rollback hazır.
- Mikro kıyaslama aracı ile doğrulayın sonunda, donanımınızdaki kazançları onaylamak için.
1) Uçuş Öncesi Güvenlik Kontrol Listesi
- Tam yedekleme:
mysqldump --single-transaction yourdb > backup.sql. - Staging ortamı üretim şemasını ve veri alt kümesini yansıtma.
- Yapı & bağımlılıklar: Mevcut FXServer yapısı, en son
oxmysql. - Kesinti penceresi üretim geçişi için (genellikle < 5 dakika).
- Sağlık kontrolleri hazır:
/players, giriş akışı, ekonomi işlemleri, garaj işlemleri, envanter işlemleri, yasak kontrolleri.
2) Kurulum ve Bağlantı oxmysql
2.1 server.cfg
# Stop using mysql-async default_prio 500 # ensure mysql-async # ← comment out or remove # Start oxmysql default_prio 50 ensure oxmysql # Connection string consumed by oxmysql set mysql_connection_string "mysql://user:pass@127.0.0.1:3306/yourdb?charset=utf8mb4" # Optional diagnostics set mysql_slow_query_warning 200 # log queries slower than 200ms set mysql_debug false # true for verbose logging during staging
Kale
mysql-asyncdevre dışı ancak hazırlık aşamasında kaynaklar klasörünüzde mevcut (hızlı geri alma için).
3) API Eşleme: mysql‑async → oxmysql
mysql-async (eski):
- Async:
MySQL.Async.fetchAll,MySQL.Async.fetchScalar,MySQL.Async.execute - Senkronizasyon:
MySQL.Sync.fetchAll,MySQL.Sync.fetchScalar,MySQL.Sync.execute - Parametreler:
@paramtabloları şu şekilde biçimlendir{ ['@identifier']=identifier }
oxmysql (modern):
- Geri çağırma stili ile export:
exports.oxmysql:query|scalar|single|insert|update(sql, params, cb) - Promise/await ile global:
MySQL.query|scalar|single|insert|update.await(sql, params)ve await olmayan geri çağırmalar.await - Parametreler: konumsal
?dizi ile veya adlandırılmış:namenesne ile
3.1 Yaygın değişiklikler
SELECT çoklu
-- mysql-async
MySQL.Async.fetchAll(
'SELECT * FROM users WHERE identifier = @id',
{ ['@id'] = identifier },
function(rows) ... end
)
-- oxmysql (callback via export)
exports.oxmysql:query(
'SELECT * FROM users WHERE identifier = ?',
{ identifier },
function(rows) ... end
)
-- oxmysql (await)
local rows = MySQL.query.await(
'SELECT * FROM users WHERE identifier = ?',
{ identifier }
)
SELECT tek satır
-- mysql-async (fetchAll + rows[1])
-- oxmysql
local row = MySQL.single.await(
'SELECT * FROM users WHERE identifier = ?',
{ identifier }
)
SELECT skaler (örneğin, count, id)
-- mysql-async
-- oxmysql
local count = MySQL.scalar.await(
'SELECT COUNT(*) FROM owned_vehicles WHERE owner = ?',
{ owner }
)
SOKMAK (insertId al)
-- mysql-async (execute)
-- oxmysql
local insertId = MySQL.insert.await(
'INSERT INTO notes (owner, text) VALUES (?, ?)',
{ cid, text }
)
UPDATE/DELETE (etkilenenSatırlar)
-- mysql-async (execute)
-- oxmysql
local changed = MySQL.update.await(
'UPDATE users SET job = ?, job_grade = ? WHERE identifier = ?',
{ job, grade, identifier }
)
İşlemler (manuel)
-- oxmysql manual transaction
MySQL.query.await('START TRANSACTION')
local ok = true
local r1 = MySQL.update.await('UPDATE users SET bank = bank - ? WHERE identifier = ? AND bank >= ?', { amount, fromId, amount })
local r2 = MySQL.update.await('UPDATE users SET bank = bank + ? WHERE identifier = ?', { amount, toId })
if r1 == 1 and r2 == 1 then
MySQL.query.await('COMMIT')
else
MySQL.query.await('ROLLBACK')
end
Bazı çerçeveler sarmalayıcılar sunar (örneğin,
ox_lib) ekleyenMySQL.ready,.transaction, vb. Yukarıdaki çağrılar ek sarmalayıcılara gerek kalmadan güvenlidir.
4) Hazır İfadeler Hile Sayfası
Parametre stilleri
- mysql‑async (eski):
@namebir tablo ile yer tutucular:{ ['@name']=value } - oxmysql (konumsal):
?yer tutucular bir dizi:{ value1, value2 } - oxmysql (adlandırılmış):
:nameyer tutucular bir nesne:{ name = value }
Örnekler
-- Named params (recommended for readability)
local row = MySQL.single.await(
'SELECT * FROM users WHERE identifier = :id',
{ id = identifier }
)
-- IN (...) list
-- Build placeholders dynamically and pass a flat array
local ids = { 'cid1','cid2','cid3' }
local qs = ('?,' ):rep(#ids):sub(1,-2) -- "?, ?, ?"
local rows = MySQL.query.await('SELECT * FROM players WHERE citizenid IN ('..qs..')', ids)
-- JSON fields (MySQL 5.7+/MariaDB 10.2+)
local name = MySQL.scalar.await('SELECT JSON_UNQUOTE(JSON_EXTRACT(data, "$.name")) FROM players WHERE citizenid = ?', { cid })
Yapmak
- Kullanmak hazır ifadeler her yerde (kullanıcı girdisini asla string birleştirmeyin).
- Tercih etmek adlandırılmış parametreler karmaşık ifadelerde netlik için.
- Eklemek LIMIT 1 tek bir varlık okunurken.
Kaçınmak
- Joker karakter
SELECT *sık erişilen yollarda (gerekli sütunları seçin). - Satır başına N+1 sorgular; toplu işlem yapın
IN (...)
IN (...).
5) Veritabanı “UP” Geçişi Scripts (Çalıştırılmaya Hazır)
Çerçevenize (ESX/QBCore) ve sunucunuza (MySQL 8+ veya MariaDB 10.4+) uygun blokları seçin. Önce hazırlık sunucusunda çalıştırın.
5.1 Karakter Setini ve Karşılaştırmayı Normalleştirin (Her yerde UTF‑8)
(A) MySQL 8+ — değiştirin yourdb bir kez
-- Force database default to utf8mb4 (emoji‑safe) ALTER DATABASE `yourdb` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; -- Convert common tables (extend list as needed) ALTER TABLE `users` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `owned_vehicles` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `players` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `player_vehicles` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
(B) MariaDB 10.4+ — aynı ifadeler geçerlidir.
Diğer sık kullanılan tabloları (envanter, fatura, telefon, topluluk, işler) sunucunuzda bulunan şekilde ekleyin.
5.2 ESX İndeksler (güvenli performans kazanımları)
MySQL 8+
ALTER TABLE `users` ADD INDEX IF NOT EXISTS `idx_users_identifier` (`identifier`), ADD INDEX IF NOT EXISTS `idx_users_job` (`job`), ADD INDEX IF NOT EXISTS `idx_users_name` (`name`); ALTER TABLE `owned_vehicles` ADD UNIQUE INDEX IF NOT EXISTS `ux_owned_vehicles_plate` (`plate`), ADD INDEX IF NOT EXISTS `idx_owned_vehicles_owner` (`owner`);
MariaDB 10.4+
-- Drop first to be idempotent where IF NOT EXISTS is unavailable DROP INDEX IF EXISTS `idx_users_identifier` ON `users`; DROP INDEX IF EXISTS `idx_users_job` ON `users`; DROP INDEX IF EXISTS `idx_users_name` ON `users`; CREATE INDEX `idx_users_identifier` ON `users` (`identifier`); CREATE INDEX `idx_users_job` ON `users` (`job`); CREATE INDEX `idx_users_name` ON `users` (`name`); DROP INDEX IF EXISTS `ux_owned_vehicles_plate` ON `owned_vehicles`; DROP INDEX IF EXISTS `idx_owned_vehicles_owner` ON `owned_vehicles`; CREATE UNIQUE INDEX `ux_owned_vehicles_plate` ON `owned_vehicles` (`plate`); CREATE INDEX `idx_owned_vehicles_owner` ON `owned_vehicles` (`owner`);
5.3 QBCore/QBOX İndeksleri
MySQL 8+
ALTER TABLE `players` ADD UNIQUE INDEX IF NOT EXISTS `ux_players_citizenid` (`citizenid`), ADD INDEX IF NOT EXISTS `idx_players_license` (`license`), ADD INDEX IF NOT EXISTS `idx_players_steam` (`steam`), ADD INDEX IF NOT EXISTS `idx_players_last_name` (`lastname`); ALTER TABLE `player_vehicles` ADD UNIQUE INDEX IF NOT EXISTS `ux_player_vehicles_plate` (`plate`), ADD INDEX IF NOT EXISTS `idx_player_vehicles_citizenid` (`citizenid`);
MariaDB 10.4+
DROP INDEX IF EXISTS `ux_players_citizenid` ON `players`; DROP INDEX IF EXISTS `idx_players_license` ON `players`; DROP INDEX IF EXISTS `idx_players_steam` ON `players`; DROP INDEX IF EXISTS `idx_players_last_name` ON `players`; CREATE UNIQUE INDEX `ux_players_citizenid` ON `players` (`citizenid`); CREATE INDEX `idx_players_license` ON `players` (`license`); CREATE INDEX `idx_players_steam` ON `players` (`steam`); CREATE INDEX `idx_players_last_name` ON `players` (`lastname`); DROP INDEX IF EXISTS `ux_player_vehicles_plate` ON `player_vehicles`; DROP INDEX IF EXISTS `idx_player_vehicles_citizenid` ON `player_vehicles`; CREATE UNIQUE INDEX `ux_player_vehicles_plate` ON `player_vehicles` (`plate`); CREATE INDEX `idx_player_vehicles_citizenid` ON `player_vehicles` (`citizenid`);
5.4 İsteğe Bağlı: ox_inventory (kuruluysa)
ALTER TABLE `ox_inventory` ADD INDEX IF NOT EXISTS `idx_inv_owner` (`owner`), ADD INDEX IF NOT EXISTS `idx_inv_type` (`type`); ALTER TABLE `ox_inventory_items` ADD INDEX IF NOT EXISTS `idx_items_inv_owner_name` (`inventory`, `owner`, `name`);
Şemanız farklıysa tablo adlarını ayarlayın (bazı kurulumlar kullanır
inventories/items).
6) Geri Alma Planı (Sıfır Panik)
6.1 Kod geri alma
- Kaynak değişikliklerinizi geri alın (bir tane saklayın
legacy-mysql-asyncdal). - İçinde
server.cfgtakas:# ensure oxmysql ensure mysql-async - FXServer'ı veya etkilenen kaynakları bağımlılık sırasına göre yeniden başlatın.
6.2 SQL geri alma
- Yalnızca indeksler eklendi), kaldırın (bkz. MariaDB yukarıdaki bloklar — kullanın
DROP INDEX IF EXISTS). - Eğer siz karakter kümesi/karşılaştırması değiştirildi ve geri almalı, veritabanını ve tabloları geri yüklemelidir:
ALTER DATABASE `yourdb` CHARACTER SET = utf8 COLLATE = utf8_general_ci; ALTER TABLE `users` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; ALTER TABLE `owned_vehicles` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; ALTER TABLE `players` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; ALTER TABLE `player_vehicles` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
Şuradan geri yüklemeyi tercih et backup.sql mümkün olduğunda toplu karakter kümesi tersine çevirmeleri yerine.
7) Uçtan Uca Geçiş Prosedürü (Betiklenebilir)
- Freeze dağıtımları, DB'yi yedekleyin.
- Uygula Bölüm 5 “UP” SQL staging'de doğrulayın → üretime geçin.
- Kod değişikliğini kaydet: çağrıları değiştir (Bölüm 3) + parametre stilleri (Bölüm 4).
- Dağıtın,
ensure oxmysql, sunucuyu yeniden başlat. - Koşmak duman testleri (smoke tests) (giriş, maaş bordroları, envanter, araç oluşturma/yok etme, yasaklar, şirket parası, iş görevi geçişleri).
- 15-30 dakika boyunca günlükleri izleyin (
mysql_slow_query_warningyardımcı olur); eksik parametreleri veya şema uyumsuzluklarını ele alın.
8) Mikro Karşılaştırmalar (Kendi Sayılarınızı Getirin)
Yol üzerindeki sorguları karşılaştırmak için ekleyebileceğiniz küçük bir kaynak senin donanım ve veri kümesi.
fxmanifest.lua
fx_version 'cerulean' game 'gta5' server_script 'bench.lua'
bench.lua
local COUNT = 2000 -- adjust for your server
local function bench(name, fn)
local t0 = os.clock()
local ok, err = pcall(fn)
local dt = (os.clock() - t0) * 1000.0
print(('[bench] %s: %.2f ms %s'):format(name, dt, ok and '' or ('ERR: '..tostring(err))))
end
-- Hot path 1: ownership lookup
bench('SELECT single', function()
for i=1,COUNT do
local row = MySQL.single.await('SELECT owner FROM owned_vehicles WHERE plate = :p LIMIT 1', { p = ('TEST%04d'):format(i % 500) })
end
end)
-- Hot path 2: batched fetch
bench('SELECT batch IN', function()
local ids = {}
for i=1,100 do ids[#ids+1] = ('cid%04d'):format(i) end
local qs = ('?,' ):rep(#ids):sub(1,-2)
local rows = MySQL.query.await('SELECT citizenid, firstname, lastname FROM players WHERE citizenid IN ('..qs..')', ids)
end)
-- Hot path 3: update with guard
bench('UPDATE guarded', function()
for i=1,COUNT do
local changed = MySQL.update.await('UPDATE users SET bank = bank + :d WHERE identifier = :id AND bank >= 0', { d = 1, id = ('lic:%04d'):format(i % 500) })
end
end)
Nasıl çalıştırılır
- Kaynağı bir klasöre koyun (örneğin,
ox-bench/), ekleyinensure ox-benchileserver.cfg. - Sunucu konsolunu takip edin; sonuçlar şu şekilde satırlar halinde yazdırılır:
[bench] SELECT single: 134.21 ms. - Bir önce/sonra karşılaştırma, ile bir kez çalıştırın
mysql-async(gerekirse çağrıları ayarla), sonraoxmysql.
Nelere dikkat etmeli
- Taşınmadan sonra bölüm başına toplam ms'yi düşürün.
- Sorgulara bağlı oyun eylemlerinde daha düşük P95/P99 gecikmesi.
- Bir saatlik canlı oyun boyunca daha az yavaş sorgu uyarısı.
9) Sorun Giderme
S: “no such export: query/single/…” hatası alıyorum.
A: oxmysql yeterince erken başlatılmamış. Emin olun ensure oxmysql onu kullanan kaynakların üzerindedir.
S: Parametre hataları veya boş sonuçlar.
C: Muhtemelen tuttunuz @param yer tutucular. Şununla değiştirin ? veya :name ve buna göre bir dizi/nesne geçirin.
S: Kilitlenmeler veya kısmi yazmalar.
C: Çok adımlı bakiyeleri/transferleri bir işlem içine alın (Bölüm 3'e bakın), Bölüm 5'teki dizinleri ekleyin.
S: JSON yolu NULL döndürüyor.
C: Motorunuzun JSON fonksiyonlarını (MySQL ≥5.7/MariaDB ≥10.2) desteklediğini ve sütun türünün JSON (Olumsuz LONGTEXT).
S: Taşıma sonrası yavaş.
C: Eksik indeksleri kontrol edin, EXPLAIN sorgunuzu, yalnızca gerekli sütunları projelendirin ve Sunucu Optimizasyonu Oyun Kitabı'nı inceleyin.
10) Kod İnceleme Kontrol Listesi (kopyala/yapıştır)
- Dize birleştirme SQL yok; tüm sorgular parametrelendirildi.
- Kullanmak
.single/.scalarileLIMIT 1yalnızca bir satır/değer gerektiğinde. - Toplu
IN (...)koleksiyonlar için okur. - Çok adımlı para/envanter işlemleri etrafındaki işlemler.
- Her sıcak için dizin mevcut
WHERE/JOINsütun. - Kaçınmak
SELECT *yoğun yollarda. - Yavaş sorguları günlüğe kaydet; en kötü sorumluları haftalık olarak takip edin.
Dahili Bağlantılar
- FiveM Sunucu Optimizasyonu: 2025'in Kesin Oyun Kitabı — https://fivemx.com/how-to-optimize-fivem-server-performance/
- Adaptör Desenleri: ESX↔QBCore↔QBOX Dışa Aktarımlar, Olaylar ve Oyuncu Modelleri — https://fivemx.com/adapter-patterns/
Krediler
fivemx.com tarafından sürdürülmektedir. Katkılar memnuniyetle karşılanır (ek güvenli indekslerin veya sarmalayıcı yardımcıların farklarını gönderin).
