perf(esx_multicharacter): look up characters by exact identifier instead of LIKE

The character selection query used `WHERE identifier LIKE 'char%:<license>'`. Every
multicharacter identifier starts with `char`, so the primary key range covers the whole
table and it degrades to a full scan on every join.

Build the exact identifiers (`char1:<license>` .. `charN:<license>` for the player's
slots) and query with `IN`, which hits the primary key directly.

Measured on a 50k row users table (MariaDB 12.3.2): rows examined 45648 -> 3, query
time 52.7 ms -> 3.2 ms. In-game the selection screen shows the same characters in the
same slots.
This commit is contained in:
Selt
2026-07-27 18:34:10 +02:00
parent 5354e278de
commit e9d5cc9f8d
2 changed files with 10 additions and 3 deletions
@@ -101,9 +101,17 @@ function Database:GetPlayerSlots(identifier)
end
function Database:GetPlayerInfo(identifier, slots)
local placeholders = {}
local identifiers = {}
for i = 1, slots do
placeholders[i] = "?"
identifiers[i] = ("%s%s:%s"):format(Server.prefix, i, identifier)
end
return MySQL.query.await(
"SELECT identifier, accounts, job, job_grade, firstname, lastname, dateofbirth, sex, skin, disabled FROM users WHERE identifier LIKE ? LIMIT ?",
{ identifier, slots })
("SELECT identifier, accounts, job, job_grade, firstname, lastname, dateofbirth, sex, skin, disabled FROM users WHERE identifier IN (%s)"):format(table.concat(placeholders, ", ")),
identifiers)
end
function Database:SetSlots(identifier, slots)
@@ -14,7 +14,6 @@ function Multicharacter:SetupCharacters(source)
ESX.Players[identifier] = source
local slots = Database:GetPlayerSlots(identifier)
identifier = Server.prefix .. "%:" .. identifier
local rawCharacters = Database:GetPlayerInfo(identifier, slots)
local characters