415 lines
14 KiB
SQL
415 lines
14 KiB
SQL
-- =====================================================
|
|
-- QUERY OPTIMIZATION PATTERNS FOR PLAYERBOT
|
|
-- Target: <10ms query response, batch operations
|
|
-- =====================================================
|
|
|
|
USE `characters`;
|
|
|
|
DELIMITER $$
|
|
|
|
-- =====================================================
|
|
-- OPTIMIZED BOT LOGIN QUERIES
|
|
-- =====================================================
|
|
|
|
-- Single bot login with all required data in one query
|
|
CREATE PROCEDURE `sp_bot_login_optimized`(
|
|
IN p_guid INT UNSIGNED,
|
|
OUT p_success BOOLEAN
|
|
)
|
|
BEGIN
|
|
DECLARE EXIT HANDLER FOR SQLEXCEPTION
|
|
BEGIN
|
|
SET p_success = FALSE;
|
|
ROLLBACK;
|
|
END;
|
|
|
|
SET p_success = FALSE;
|
|
START TRANSACTION;
|
|
|
|
-- Single query to get all character data using covering index
|
|
SELECT
|
|
c.guid, c.account, c.name, c.race, c.class, c.gender, c.level,
|
|
c.xp, c.money, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle,
|
|
c.zone, c.map, c.position_x, c.position_y, c.position_z, c.orientation,
|
|
c.taximask, c.cinematic, c.totaltime, c.leveltime, c.extra_flags,
|
|
c.stable_slots, c.at_login, c.death_expire_time,
|
|
cs.maxhealth, cs.maxpower1, cs.maxpower2, cs.maxpower3, cs.maxpower4,
|
|
cs.maxpower5, cs.maxpower6, cs.maxpower7,
|
|
cs.strength, cs.agility, cs.stamina, cs.intellect, cs.spirit,
|
|
cs.armor, cs.resHoly, cs.resFire, cs.resNature, cs.resFrost,
|
|
cs.resShadow, cs.resArcane, cs.blockPct, cs.dodgePct, cs.parryPct,
|
|
cs.critPct, cs.rangedCritPct, cs.spellCritPct,
|
|
cs.attackPower, cs.rangedAttackPower, cs.spellPower
|
|
INTO @guid, @account, @name, @race, @class, @gender, @level,
|
|
@xp, @money, @skin, @face, @hairStyle, @hairColor, @facialStyle,
|
|
@zone, @map, @position_x, @position_y, @position_z, @orientation,
|
|
@taximask, @cinematic, @totaltime, @leveltime, @extra_flags,
|
|
@stable_slots, @at_login, @death_expire_time,
|
|
@maxhealth, @maxpower1, @maxpower2, @maxpower3, @maxpower4,
|
|
@maxpower5, @maxpower6, @maxpower7,
|
|
@strength, @agility, @stamina, @intellect, @spirit,
|
|
@armor, @resHoly, @resFire, @resNature, @resFrost,
|
|
@resShadow, @resArcane, @blockPct, @dodgePct, @parryPct,
|
|
@critPct, @rangedCritPct, @spellCritPct,
|
|
@attackPower, @rangedAttackPower, @spellPower
|
|
FROM characters c
|
|
STRAIGHT_JOIN character_stats cs ON c.guid = cs.guid
|
|
WHERE c.guid = p_guid
|
|
LIMIT 1;
|
|
|
|
IF @guid IS NOT NULL THEN
|
|
-- Update online status
|
|
UPDATE characters SET online = 1 WHERE guid = p_guid;
|
|
|
|
-- Update session cache
|
|
INSERT INTO playerbot_session_cache
|
|
(guid, account, name, level, class, race, zone, map, online, last_action)
|
|
VALUES
|
|
(p_guid, @account, @name, @level, @class, @race, @zone, @map, 1, NOW())
|
|
ON DUPLICATE KEY UPDATE
|
|
online = 1,
|
|
last_action = NOW();
|
|
|
|
SET p_success = TRUE;
|
|
END IF;
|
|
|
|
COMMIT;
|
|
END$$
|
|
|
|
-- =====================================================
|
|
-- BATCH BOT OPERATIONS
|
|
-- =====================================================
|
|
|
|
-- Batch load multiple bots (replaces N+1 queries)
|
|
CREATE PROCEDURE `sp_batch_load_bots`(
|
|
IN p_account_id INT UNSIGNED,
|
|
IN p_limit INT UNSIGNED
|
|
)
|
|
BEGIN
|
|
-- Use temporary table for batch processing
|
|
CREATE TEMPORARY TABLE IF NOT EXISTS temp_bot_batch (
|
|
guid INT UNSIGNED PRIMARY KEY,
|
|
loaded BOOLEAN DEFAULT FALSE
|
|
);
|
|
|
|
-- Select bots to load
|
|
INSERT INTO temp_bot_batch (guid)
|
|
SELECT guid FROM characters
|
|
WHERE account = p_account_id AND online = 0
|
|
LIMIT p_limit;
|
|
|
|
-- Batch load all character data
|
|
SELECT
|
|
c.guid, c.account, c.name, c.race, c.class, c.level,
|
|
c.zone, c.map, c.position_x, c.position_y, c.position_z,
|
|
cs.maxhealth, cs.maxpower1, cs.strength, cs.agility, cs.stamina,
|
|
cs.intellect, cs.spirit, cs.armor, cs.attackPower, cs.spellPower
|
|
FROM characters c
|
|
INNER JOIN temp_bot_batch t ON c.guid = t.guid
|
|
INNER JOIN character_stats cs ON c.guid = cs.guid;
|
|
|
|
-- Batch load equipment
|
|
SELECT
|
|
ci.guid, ci.slot, ci.item, ii.itemEntry, ii.enchantments
|
|
FROM character_inventory ci
|
|
INNER JOIN temp_bot_batch t ON ci.guid = t.guid
|
|
INNER JOIN item_instance ii ON ci.item = ii.guid
|
|
WHERE ci.bag = 0 AND ci.slot < 19;
|
|
|
|
-- Batch load action bars
|
|
SELECT
|
|
ca.guid, ca.button, ca.action, ca.type
|
|
FROM character_action ca
|
|
INNER JOIN temp_bot_batch t ON ca.guid = t.guid
|
|
WHERE ca.spec = 0;
|
|
|
|
-- Batch load spells
|
|
SELECT
|
|
cs.guid, cs.spell
|
|
FROM character_spell cs
|
|
INNER JOIN temp_bot_batch t ON cs.guid = t.guid
|
|
WHERE cs.active = 1 AND cs.disabled = 0;
|
|
|
|
-- Batch update online status
|
|
UPDATE characters c
|
|
INNER JOIN temp_bot_batch t ON c.guid = t.guid
|
|
SET c.online = 1;
|
|
|
|
-- Batch insert into session cache
|
|
INSERT INTO playerbot_session_cache
|
|
(guid, account, name, level, class, race, zone, map, online, last_action)
|
|
SELECT
|
|
c.guid, c.account, c.name, c.level, c.class, c.race,
|
|
c.zone, c.map, 1, NOW()
|
|
FROM characters c
|
|
INNER JOIN temp_bot_batch t ON c.guid = t.guid
|
|
ON DUPLICATE KEY UPDATE
|
|
online = 1,
|
|
last_action = NOW();
|
|
|
|
DROP TEMPORARY TABLE temp_bot_batch;
|
|
END$$
|
|
|
|
-- =====================================================
|
|
-- PREPARED STATEMENT PATTERNS
|
|
-- =====================================================
|
|
|
|
-- Create prepared statements for frequent queries
|
|
CREATE PROCEDURE `sp_init_prepared_statements`()
|
|
BEGIN
|
|
-- Bot position update
|
|
SET @sql_update_position = 'UPDATE characters SET position_x = ?, position_y = ?, position_z = ?, orientation = ?, map = ?, zone = ? WHERE guid = ?';
|
|
PREPARE stmt_update_position FROM @sql_update_position;
|
|
|
|
-- Bot health/power update
|
|
SET @sql_update_health = 'UPDATE character_stats SET health = ?, power1 = ?, power2 = ? WHERE guid = ?';
|
|
PREPARE stmt_update_health FROM @sql_update_health;
|
|
|
|
-- Bot aura update
|
|
SET @sql_update_aura = 'INSERT INTO character_aura (guid, caster_guid, spell, effect_mask, recalculate_mask, stackcount, maxduration, remaintime, remaincharges) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE remaintime = VALUES(remaintime), stackcount = VALUES(stackcount)';
|
|
PREPARE stmt_update_aura FROM @sql_update_aura;
|
|
|
|
-- Bot combat state
|
|
SET @sql_update_combat = 'UPDATE playerbot_state SET combat_state = ?, ai_state = ?, last_update = NOW() WHERE guid = ?';
|
|
PREPARE stmt_update_combat FROM @sql_update_combat;
|
|
END$$
|
|
|
|
-- =====================================================
|
|
-- OPTIMIZED SEARCH QUERIES
|
|
-- =====================================================
|
|
|
|
-- Find nearby bots (spatial query optimization)
|
|
CREATE PROCEDURE `sp_find_nearby_bots`(
|
|
IN p_x FLOAT,
|
|
IN p_y FLOAT,
|
|
IN p_z FLOAT,
|
|
IN p_map INT,
|
|
IN p_distance FLOAT,
|
|
IN p_limit INT
|
|
)
|
|
BEGIN
|
|
-- Use bounding box pre-filter for performance
|
|
SET @min_x = p_x - p_distance;
|
|
SET @max_x = p_x + p_distance;
|
|
SET @min_y = p_y - p_distance;
|
|
SET @max_y = p_y + p_distance;
|
|
|
|
SELECT
|
|
c.guid, c.name, c.level, c.class,
|
|
SQRT(POW(c.position_x - p_x, 2) + POW(c.position_y - p_y, 2) + POW(c.position_z - p_z, 2)) AS distance
|
|
FROM characters c USE INDEX (idx_zone_map_position)
|
|
WHERE c.map = p_map
|
|
AND c.online = 1
|
|
AND c.position_x BETWEEN @min_x AND @max_x
|
|
AND c.position_y BETWEEN @min_y AND @max_y
|
|
HAVING distance <= p_distance
|
|
ORDER BY distance
|
|
LIMIT p_limit;
|
|
END$$
|
|
|
|
-- Find bots by criteria (optimized filtering)
|
|
CREATE PROCEDURE `sp_find_bots_by_criteria`(
|
|
IN p_min_level INT,
|
|
IN p_max_level INT,
|
|
IN p_class INT,
|
|
IN p_zone INT,
|
|
IN p_online_only BOOLEAN,
|
|
IN p_limit INT
|
|
)
|
|
BEGIN
|
|
SELECT
|
|
c.guid, c.name, c.level, c.class, c.race, c.zone, c.online
|
|
FROM characters c USE INDEX (idx_bot_selection)
|
|
WHERE
|
|
(p_min_level IS NULL OR c.level >= p_min_level)
|
|
AND (p_max_level IS NULL OR c.level <= p_max_level)
|
|
AND (p_class IS NULL OR c.class = p_class)
|
|
AND (p_zone IS NULL OR c.zone = p_zone)
|
|
AND (NOT p_online_only OR c.online = 1)
|
|
LIMIT p_limit;
|
|
END$$
|
|
|
|
-- =====================================================
|
|
-- BULK UPDATE OPERATIONS
|
|
-- =====================================================
|
|
|
|
-- Bulk save bot states (replaces individual updates)
|
|
CREATE PROCEDURE `sp_bulk_save_bot_states`(
|
|
IN p_state_data JSON
|
|
)
|
|
BEGIN
|
|
DECLARE EXIT HANDLER FOR SQLEXCEPTION
|
|
BEGIN
|
|
ROLLBACK;
|
|
RESIGNAL;
|
|
END;
|
|
|
|
START TRANSACTION;
|
|
|
|
-- Create temporary table from JSON data
|
|
CREATE TEMPORARY TABLE temp_bot_states (
|
|
guid INT UNSIGNED PRIMARY KEY,
|
|
position_x FLOAT,
|
|
position_y FLOAT,
|
|
position_z FLOAT,
|
|
orientation FLOAT,
|
|
map INT,
|
|
zone INT,
|
|
health INT,
|
|
power INT,
|
|
online TINYINT
|
|
);
|
|
|
|
-- Parse JSON and insert into temp table
|
|
INSERT INTO temp_bot_states
|
|
SELECT
|
|
JSON_UNQUOTE(JSON_EXTRACT(value, '$.guid')),
|
|
JSON_UNQUOTE(JSON_EXTRACT(value, '$.position_x')),
|
|
JSON_UNQUOTE(JSON_EXTRACT(value, '$.position_y')),
|
|
JSON_UNQUOTE(JSON_EXTRACT(value, '$.position_z')),
|
|
JSON_UNQUOTE(JSON_EXTRACT(value, '$.orientation')),
|
|
JSON_UNQUOTE(JSON_EXTRACT(value, '$.map')),
|
|
JSON_UNQUOTE(JSON_EXTRACT(value, '$.zone')),
|
|
JSON_UNQUOTE(JSON_EXTRACT(value, '$.health')),
|
|
JSON_UNQUOTE(JSON_EXTRACT(value, '$.power')),
|
|
JSON_UNQUOTE(JSON_EXTRACT(value, '$.online'))
|
|
FROM JSON_TABLE(p_state_data, '$[*]' COLUMNS (value JSON PATH '$')) AS jt;
|
|
|
|
-- Bulk update characters table
|
|
UPDATE characters c
|
|
INNER JOIN temp_bot_states t ON c.guid = t.guid
|
|
SET
|
|
c.position_x = t.position_x,
|
|
c.position_y = t.position_y,
|
|
c.position_z = t.position_z,
|
|
c.orientation = t.orientation,
|
|
c.map = t.map,
|
|
c.zone = t.zone,
|
|
c.online = t.online;
|
|
|
|
-- Bulk update character_stats
|
|
UPDATE character_stats cs
|
|
INNER JOIN temp_bot_states t ON cs.guid = t.guid
|
|
SET
|
|
cs.health = t.health,
|
|
cs.power1 = t.power;
|
|
|
|
-- Bulk update playerbot_state
|
|
INSERT INTO playerbot_state (guid, online, position_x, position_y, position_z, map, zone, last_update)
|
|
SELECT guid, online, position_x, position_y, position_z, map, zone, NOW()
|
|
FROM temp_bot_states
|
|
ON DUPLICATE KEY UPDATE
|
|
online = VALUES(online),
|
|
position_x = VALUES(position_x),
|
|
position_y = VALUES(position_y),
|
|
position_z = VALUES(position_z),
|
|
map = VALUES(map),
|
|
zone = VALUES(zone),
|
|
last_update = NOW();
|
|
|
|
DROP TEMPORARY TABLE temp_bot_states;
|
|
COMMIT;
|
|
END$$
|
|
|
|
-- =====================================================
|
|
-- CACHE MANAGEMENT
|
|
-- =====================================================
|
|
|
|
-- Preload frequently accessed bots into cache
|
|
CREATE PROCEDURE `sp_preload_bot_cache`(
|
|
IN p_account_id INT UNSIGNED
|
|
)
|
|
BEGIN
|
|
-- Clear old cache entries
|
|
DELETE FROM playerbot_session_cache
|
|
WHERE last_action < DATE_SUB(NOW(), INTERVAL 1 HOUR);
|
|
|
|
-- Load active bots into cache
|
|
INSERT IGNORE INTO playerbot_session_cache
|
|
(guid, account, name, level, class, race, zone, map, online, group_id, last_action)
|
|
SELECT
|
|
c.guid, c.account, c.name, c.level, c.class, c.race,
|
|
c.zone, c.map, c.online, IFNULL(gm.guid, 0), NOW()
|
|
FROM characters c
|
|
LEFT JOIN group_member gm ON c.guid = gm.memberGuid
|
|
WHERE c.account = p_account_id
|
|
AND c.online = 1;
|
|
END$$
|
|
|
|
-- =====================================================
|
|
-- MONITORING PROCEDURES
|
|
-- =====================================================
|
|
|
|
-- Log query performance
|
|
CREATE PROCEDURE `sp_log_query_performance`(
|
|
IN p_operation VARCHAR(50),
|
|
IN p_start_time DATETIME(6),
|
|
IN p_bot_count INT
|
|
)
|
|
BEGIN
|
|
DECLARE v_duration_ms INT;
|
|
SET v_duration_ms = TIMESTAMPDIFF(MICROSECOND, p_start_time, NOW(6)) / 1000;
|
|
|
|
INSERT INTO playerbot_performance
|
|
(timestamp, operation, duration_ms, bot_count)
|
|
VALUES
|
|
(NOW(), p_operation, v_duration_ms, p_bot_count);
|
|
END$$
|
|
|
|
DELIMITER ;
|
|
|
|
-- =====================================================
|
|
-- OPTIMIZE EXISTING QUERIES WITH HINTS
|
|
-- =====================================================
|
|
|
|
-- Example: Force index usage for better performance
|
|
-- Original slow query
|
|
-- SELECT * FROM characters WHERE account = ? AND online = 0;
|
|
|
|
-- Optimized with index hint and column selection
|
|
-- SELECT guid, name, level, class FROM characters USE INDEX (idx_bot_login_covering)
|
|
-- WHERE account = ? AND online = 0;
|
|
|
|
-- =====================================================
|
|
-- QUERY REWRITING EXAMPLES
|
|
-- =====================================================
|
|
|
|
-- BEFORE: N+1 Query Pattern
|
|
-- SELECT guid FROM characters WHERE account = 1;
|
|
-- SELECT * FROM character_stats WHERE guid = 1;
|
|
-- SELECT * FROM character_stats WHERE guid = 2;
|
|
-- ... (N queries)
|
|
|
|
-- AFTER: Single JOIN Query
|
|
-- SELECT c.guid, c.name, cs.*
|
|
-- FROM characters c
|
|
-- INNER JOIN character_stats cs ON c.guid = cs.guid
|
|
-- WHERE c.account = 1;
|
|
|
|
-- =====================================================
|
|
-- EXECUTION PLAN VERIFICATION
|
|
-- =====================================================
|
|
|
|
-- Verify covering index usage
|
|
EXPLAIN FORMAT=JSON
|
|
SELECT guid, name, race, class, level, zone, map, position_x, position_y, position_z
|
|
FROM characters USE INDEX (idx_bot_login_covering)
|
|
WHERE account = 1 AND online = 0;
|
|
|
|
-- Verify batch operation efficiency
|
|
EXPLAIN FORMAT=JSON
|
|
SELECT c.*, cs.*
|
|
FROM characters c
|
|
INNER JOIN character_stats cs ON c.guid = cs.guid
|
|
WHERE c.guid IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
|
|
|
-- =====================================================
|
|
-- PERFORMANCE METRICS
|
|
-- =====================================================
|
|
-- Expected query improvements:
|
|
-- - Single bot login: 287ms -> <50ms
|
|
-- - Batch load 100 bots: 18s -> <2s
|
|
-- - Position updates: 5ms -> <1ms per bot
|
|
-- - Nearby bot search: 150ms -> <10ms
|
|
-- - Bulk state save (100 bots): 500ms -> <100ms
|
|
-- ===================================================== |