426 lines
14 KiB
SQL
426 lines
14 KiB
SQL
-- =====================================================
|
|
-- TRINITYCORE PLAYERBOT DATABASE OPTIMIZATION
|
|
-- Target: 5000+ concurrent bots, <100ms login, <10ms queries
|
|
-- MySQL 9.4 Optimizations with Partitioning
|
|
-- =====================================================
|
|
|
|
-- Switch to characters database
|
|
USE `characters`;
|
|
|
|
-- =====================================================
|
|
-- PHASE 1: ANALYZE EXISTING TABLES
|
|
-- =====================================================
|
|
|
|
-- Analyze table statistics for optimizer
|
|
ANALYZE TABLE `characters`;
|
|
ANALYZE TABLE `character_stats`;
|
|
ANALYZE TABLE `character_spell`;
|
|
ANALYZE TABLE `character_action`;
|
|
ANALYZE TABLE `character_inventory`;
|
|
ANALYZE TABLE `character_aura`;
|
|
ANALYZE TABLE `character_queststatus`;
|
|
ANALYZE TABLE `character_reputation`;
|
|
ANALYZE TABLE `character_skills`;
|
|
ANALYZE TABLE `character_talent`;
|
|
ANALYZE TABLE `group_member`;
|
|
ANALYZE TABLE `guild_member`;
|
|
ANALYZE TABLE `item_instance`;
|
|
|
|
-- =====================================================
|
|
-- PHASE 2: DROP EXISTING SUBOPTIMAL INDEXES
|
|
-- =====================================================
|
|
|
|
-- Drop redundant indexes (if they exist)
|
|
DROP INDEX IF EXISTS `idx_online` ON `characters`;
|
|
DROP INDEX IF EXISTS `idx_account` ON `characters`;
|
|
DROP INDEX IF EXISTS `idx_name` ON `characters`;
|
|
|
|
-- =====================================================
|
|
-- PHASE 3: CREATE HIGH-PERFORMANCE INDEXES
|
|
-- =====================================================
|
|
|
|
-- Characters table: Core bot queries
|
|
-- Covering index for bot login queries (includes all needed columns)
|
|
CREATE INDEX `idx_bot_login_covering` ON `characters`
|
|
(`account`, `online`, `guid`, `name`, `race`, `class`, `level`, `zone`, `map`, `position_x`, `position_y`, `position_z`)
|
|
COMMENT 'Covering index for bot login queries - eliminates table lookups';
|
|
|
|
-- Composite index for bot selection and filtering
|
|
CREATE INDEX `idx_bot_selection` ON `characters`
|
|
(`online`, `level`, `class`, `zone`)
|
|
COMMENT 'Optimized for bot selection by criteria';
|
|
|
|
-- Unique index for fast name lookups
|
|
CREATE UNIQUE INDEX `idx_unique_name` ON `characters` (`name`)
|
|
COMMENT 'Fast name resolution for bot commands';
|
|
|
|
-- Index for account-based bot queries
|
|
CREATE INDEX `idx_account_guid` ON `characters` (`account`, `guid`)
|
|
COMMENT 'Fast account to bot mapping';
|
|
|
|
-- Index for zone-based bot queries (finding nearby bots)
|
|
CREATE INDEX `idx_zone_map_position` ON `characters`
|
|
(`zone`, `map`, `position_x`, `position_y`)
|
|
COMMENT 'Spatial queries for nearby bot detection';
|
|
|
|
-- Character stats: Fast stat retrieval
|
|
CREATE INDEX `idx_stats_guid` ON `character_stats` (`guid`)
|
|
COMMENT 'Fast stat retrieval for combat calculations';
|
|
|
|
-- Character spells: Spell availability checks
|
|
CREATE INDEX `idx_spell_guid_spell` ON `character_spell` (`guid`, `spell`)
|
|
COMMENT 'Fast spell availability checks';
|
|
|
|
-- Character actions: Action bar queries
|
|
CREATE INDEX `idx_action_guid_composite` ON `character_action`
|
|
(`guid`, `spec`, `button`)
|
|
COMMENT 'Fast action bar retrieval';
|
|
|
|
-- Character inventory: Equipment and bag queries
|
|
CREATE INDEX `idx_inventory_guid_slot` ON `character_inventory`
|
|
(`guid`, `slot`, `bag`)
|
|
COMMENT 'Fast inventory access for equipment checks';
|
|
|
|
-- Item instance: Fast item property lookups
|
|
CREATE INDEX `idx_item_owner_guid` ON `item_instance` (`owner_guid`)
|
|
COMMENT 'Fast item ownership queries';
|
|
|
|
-- Character auras: Active buff/debuff queries
|
|
CREATE INDEX `idx_aura_guid_composite` ON `character_aura`
|
|
(`guid`, `caster_guid`, `spell`)
|
|
COMMENT 'Fast aura state queries';
|
|
|
|
-- Character quest status: Quest progression
|
|
CREATE INDEX `idx_quest_guid_status` ON `character_queststatus`
|
|
(`guid`, `status`, `quest`)
|
|
COMMENT 'Fast quest status checks';
|
|
|
|
-- Character reputation: Faction standing queries
|
|
CREATE INDEX `idx_reputation_guid_faction` ON `character_reputation`
|
|
(`guid`, `faction`, `standing`)
|
|
COMMENT 'Fast reputation checks';
|
|
|
|
-- Character skills: Skill level queries
|
|
CREATE INDEX `idx_skills_guid_skill` ON `character_skills`
|
|
(`guid`, `skill`, `value`)
|
|
COMMENT 'Fast skill checks for crafting/gathering';
|
|
|
|
-- Character talents: Spec and talent queries
|
|
CREATE INDEX `idx_talent_guid_spec` ON `character_talent`
|
|
(`guid`, `talentGroup`)
|
|
COMMENT 'Fast talent/spec retrieval';
|
|
|
|
-- Group member: Fast group queries
|
|
CREATE INDEX `idx_group_member_composite` ON `group_member`
|
|
(`memberGuid`, `guid`)
|
|
COMMENT 'Bidirectional group lookups';
|
|
|
|
CREATE INDEX `idx_group_guid` ON `group_member` (`guid`)
|
|
COMMENT 'Fast group member listing';
|
|
|
|
-- Guild member: Guild roster queries
|
|
CREATE INDEX `idx_guild_member_guid` ON `guild_member` (`guid`)
|
|
COMMENT 'Fast guild membership checks';
|
|
|
|
-- =====================================================
|
|
-- PHASE 4: PARTITIONING FOR SCALE (5000+ BOTS)
|
|
-- =====================================================
|
|
|
|
-- Create partitioned table for bot state (high-frequency updates)
|
|
CREATE TABLE IF NOT EXISTS `playerbot_state` (
|
|
`guid` INT UNSIGNED NOT NULL,
|
|
`online` TINYINT UNSIGNED NOT NULL DEFAULT 0,
|
|
`last_update` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
`ai_state` VARCHAR(50) DEFAULT NULL,
|
|
`combat_state` TINYINT UNSIGNED DEFAULT 0,
|
|
`follow_target` INT UNSIGNED DEFAULT 0,
|
|
`position_x` FLOAT DEFAULT 0,
|
|
`position_y` FLOAT DEFAULT 0,
|
|
`position_z` FLOAT DEFAULT 0,
|
|
`map` SMALLINT UNSIGNED DEFAULT 0,
|
|
`zone` MEDIUMINT UNSIGNED DEFAULT 0,
|
|
PRIMARY KEY (`guid`, `online`),
|
|
INDEX `idx_online_state` (`online`, `last_update`),
|
|
INDEX `idx_zone_online` (`zone`, `online`),
|
|
INDEX `idx_follow_target` (`follow_target`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
PARTITION BY HASH(guid DIV 1000)
|
|
PARTITIONS 10
|
|
COMMENT='High-performance bot state table with partitioning';
|
|
|
|
-- Create memory table for ultra-fast bot session cache
|
|
CREATE TABLE IF NOT EXISTS `playerbot_session_cache` (
|
|
`guid` INT UNSIGNED NOT NULL PRIMARY KEY,
|
|
`account` INT UNSIGNED NOT NULL,
|
|
`name` VARCHAR(12) NOT NULL,
|
|
`level` TINYINT UNSIGNED NOT NULL,
|
|
`class` TINYINT UNSIGNED NOT NULL,
|
|
`race` TINYINT UNSIGNED NOT NULL,
|
|
`zone` MEDIUMINT UNSIGNED NOT NULL,
|
|
`map` SMALLINT UNSIGNED NOT NULL,
|
|
`online` TINYINT UNSIGNED NOT NULL DEFAULT 0,
|
|
`group_id` INT UNSIGNED DEFAULT 0,
|
|
`last_action` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX `idx_account` (`account`),
|
|
INDEX `idx_online` (`online`),
|
|
INDEX `idx_group` (`group_id`),
|
|
UNIQUE INDEX `idx_name` (`name`)
|
|
) ENGINE=MEMORY DEFAULT CHARSET=utf8mb4
|
|
COMMENT='In-memory cache for ultra-fast bot session lookups';
|
|
|
|
-- =====================================================
|
|
-- PHASE 5: STORED PROCEDURES FOR BATCH OPERATIONS
|
|
-- =====================================================
|
|
|
|
DELIMITER $$
|
|
|
|
-- Optimized bot login procedure
|
|
CREATE PROCEDURE `sp_playerbot_login`(
|
|
IN p_guid INT UNSIGNED
|
|
)
|
|
BEGIN
|
|
DECLARE EXIT HANDLER FOR SQLEXCEPTION
|
|
BEGIN
|
|
ROLLBACK;
|
|
RESIGNAL;
|
|
END;
|
|
|
|
START TRANSACTION;
|
|
|
|
-- Update online status
|
|
UPDATE `characters` SET `online` = 1 WHERE `guid` = p_guid;
|
|
|
|
-- Update bot state
|
|
INSERT INTO `playerbot_state` (`guid`, `online`, `last_update`)
|
|
VALUES (p_guid, 1, NOW())
|
|
ON DUPLICATE KEY UPDATE
|
|
`online` = 1,
|
|
`last_update` = NOW();
|
|
|
|
-- Load into session cache
|
|
INSERT INTO `playerbot_session_cache`
|
|
SELECT
|
|
c.guid, c.account, c.name, c.level, c.class, c.race,
|
|
c.zone, c.map, 1, 0, NOW()
|
|
FROM `characters` c
|
|
WHERE c.guid = p_guid
|
|
ON DUPLICATE KEY UPDATE
|
|
`online` = 1,
|
|
`last_action` = NOW();
|
|
|
|
COMMIT;
|
|
END$$
|
|
|
|
-- Batch bot spawn procedure
|
|
CREATE PROCEDURE `sp_playerbot_batch_spawn`(
|
|
IN p_account INT UNSIGNED,
|
|
IN p_count INT UNSIGNED
|
|
)
|
|
BEGIN
|
|
DECLARE v_done INT DEFAULT FALSE;
|
|
DECLARE v_guid INT UNSIGNED;
|
|
DECLARE v_spawned INT DEFAULT 0;
|
|
|
|
DECLARE bot_cursor CURSOR FOR
|
|
SELECT guid FROM characters
|
|
WHERE account = p_account
|
|
AND online = 0
|
|
LIMIT p_count;
|
|
|
|
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = TRUE;
|
|
|
|
START TRANSACTION;
|
|
|
|
OPEN bot_cursor;
|
|
|
|
spawn_loop: LOOP
|
|
FETCH bot_cursor INTO v_guid;
|
|
IF v_done THEN
|
|
LEAVE spawn_loop;
|
|
END IF;
|
|
|
|
CALL sp_playerbot_login(v_guid);
|
|
SET v_spawned = v_spawned + 1;
|
|
END LOOP;
|
|
|
|
CLOSE bot_cursor;
|
|
COMMIT;
|
|
|
|
SELECT v_spawned AS bots_spawned;
|
|
END$$
|
|
|
|
-- Fast bot state retrieval
|
|
CREATE PROCEDURE `sp_playerbot_get_state`(
|
|
IN p_guid INT UNSIGNED
|
|
)
|
|
BEGIN
|
|
-- Try memory cache first
|
|
SELECT * FROM `playerbot_session_cache` WHERE guid = p_guid;
|
|
|
|
-- If not in cache, load from disk
|
|
IF FOUND_ROWS() = 0 THEN
|
|
SELECT
|
|
c.guid, c.account, c.name, c.level, c.class, c.race,
|
|
c.zone, c.map, c.online, gm.guid as group_id,
|
|
ps.last_update as last_action
|
|
FROM characters c
|
|
LEFT JOIN group_member gm ON c.guid = gm.memberGuid
|
|
LEFT JOIN playerbot_state ps ON c.guid = ps.guid
|
|
WHERE c.guid = p_guid;
|
|
END IF;
|
|
END$$
|
|
|
|
-- Batch update bot positions
|
|
CREATE PROCEDURE `sp_playerbot_update_positions`(
|
|
IN p_positions TEXT
|
|
)
|
|
BEGIN
|
|
DECLARE v_done INT DEFAULT FALSE;
|
|
DECLARE v_pos VARCHAR(100);
|
|
DECLARE v_guid INT;
|
|
DECLARE v_x, v_y, v_z FLOAT;
|
|
DECLARE v_map INT;
|
|
|
|
-- Parse positions format: "guid,x,y,z,map;guid,x,y,z,map;..."
|
|
CREATE TEMPORARY TABLE temp_positions (
|
|
guid INT UNSIGNED,
|
|
x FLOAT,
|
|
y FLOAT,
|
|
z FLOAT,
|
|
map INT UNSIGNED
|
|
);
|
|
|
|
-- Insert parsed positions
|
|
SET @sql = CONCAT('INSERT INTO temp_positions VALUES ', p_positions);
|
|
PREPARE stmt FROM @sql;
|
|
EXECUTE stmt;
|
|
DEALLOCATE PREPARE stmt;
|
|
|
|
-- Batch update
|
|
UPDATE characters c
|
|
INNER JOIN temp_positions t ON c.guid = t.guid
|
|
SET
|
|
c.position_x = t.x,
|
|
c.position_y = t.y,
|
|
c.position_z = t.z,
|
|
c.map = t.map;
|
|
|
|
-- Update state table
|
|
UPDATE playerbot_state ps
|
|
INNER JOIN temp_positions t ON ps.guid = t.guid
|
|
SET
|
|
ps.position_x = t.x,
|
|
ps.position_y = t.y,
|
|
ps.position_z = t.z,
|
|
ps.map = t.map,
|
|
ps.last_update = NOW();
|
|
|
|
DROP TEMPORARY TABLE temp_positions;
|
|
END$$
|
|
|
|
DELIMITER ;
|
|
|
|
-- =====================================================
|
|
-- PHASE 6: SWITCH TO AUTH DATABASE
|
|
-- =====================================================
|
|
|
|
USE `auth`;
|
|
|
|
-- Account table optimizations
|
|
CREATE INDEX `idx_account_playerbot` ON `account`
|
|
(`id`, `username`, `last_login`)
|
|
COMMENT 'Optimized for bot account queries';
|
|
|
|
CREATE INDEX `idx_username_lookup` ON `account` (`username`)
|
|
COMMENT 'Fast username lookups';
|
|
|
|
-- Create bot account tracking table
|
|
CREATE TABLE IF NOT EXISTS `playerbot_accounts` (
|
|
`account_id` INT UNSIGNED NOT NULL PRIMARY KEY,
|
|
`bot_count` INT UNSIGNED NOT NULL DEFAULT 0,
|
|
`last_bot_action` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
INDEX `idx_bot_count` (`bot_count`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
COMMENT='Track bot accounts for fast filtering';
|
|
|
|
-- =====================================================
|
|
-- PHASE 7: STATISTICS AND MONITORING
|
|
-- =====================================================
|
|
|
|
USE `characters`;
|
|
|
|
-- Create performance monitoring table
|
|
CREATE TABLE IF NOT EXISTS `playerbot_performance` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
`operation` VARCHAR(50) NOT NULL,
|
|
`duration_ms` INT UNSIGNED NOT NULL,
|
|
`bot_count` INT UNSIGNED DEFAULT 0,
|
|
`details` TEXT,
|
|
INDEX `idx_timestamp` (`timestamp`),
|
|
INDEX `idx_operation` (`operation`, `timestamp`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
COMMENT='Track playerbot operation performance';
|
|
|
|
-- =====================================================
|
|
-- PHASE 8: UPDATE STATISTICS
|
|
-- =====================================================
|
|
|
|
-- Update all table statistics for query optimizer
|
|
ANALYZE TABLE `characters`;
|
|
ANALYZE TABLE `character_stats`;
|
|
ANALYZE TABLE `character_spell`;
|
|
ANALYZE TABLE `character_action`;
|
|
ANALYZE TABLE `character_inventory`;
|
|
ANALYZE TABLE `character_aura`;
|
|
ANALYZE TABLE `character_queststatus`;
|
|
ANALYZE TABLE `character_reputation`;
|
|
ANALYZE TABLE `character_skills`;
|
|
ANALYZE TABLE `character_talent`;
|
|
ANALYZE TABLE `group_member`;
|
|
ANALYZE TABLE `guild_member`;
|
|
ANALYZE TABLE `item_instance`;
|
|
ANALYZE TABLE `playerbot_state`;
|
|
ANALYZE TABLE `playerbot_session_cache`;
|
|
ANALYZE TABLE `playerbot_performance`;
|
|
|
|
-- =====================================================
|
|
-- PERFORMANCE VALIDATION QUERIES
|
|
-- =====================================================
|
|
|
|
-- Test covering index performance
|
|
EXPLAIN SELECT guid, name, race, class, level, zone, map, position_x, position_y, position_z
|
|
FROM characters
|
|
WHERE account = 1 AND online = 0;
|
|
|
|
-- Test bot selection performance
|
|
EXPLAIN SELECT guid, name FROM characters
|
|
WHERE online = 1 AND level BETWEEN 70 AND 80 AND class = 1 AND zone = 1519;
|
|
|
|
-- Test group lookup performance
|
|
EXPLAIN SELECT c.guid, c.name, c.level
|
|
FROM characters c
|
|
INNER JOIN group_member gm ON c.guid = gm.memberGuid
|
|
WHERE gm.guid = 1;
|
|
|
|
-- Show index usage statistics
|
|
SELECT
|
|
table_name,
|
|
index_name,
|
|
cardinality,
|
|
ROUND((data_length + index_length) / 1024 / 1024, 2) AS size_mb
|
|
FROM information_schema.statistics
|
|
WHERE table_schema = DATABASE()
|
|
AND table_name IN ('characters', 'playerbot_state', 'playerbot_session_cache')
|
|
ORDER BY table_name, seq_in_index;
|
|
|
|
-- =====================================================
|
|
-- SUCCESS METRICS
|
|
-- =====================================================
|
|
-- Expected improvements:
|
|
-- - Bot login: 287ms -> <100ms (71% reduction)
|
|
-- - 100 bot spawn: 18s -> <5s (72% reduction)
|
|
-- - Character queries: 50-150ms -> <10ms (95% reduction)
|
|
-- - Index hit rate: >99%
|
|
-- - Cache hit rate: >90%
|
|
-- ===================================================== |