mysql - 优化一个巨大的 MySQL View

标签 mysql sql database view query-optimization

我为 friend 开发了一个用于记录游戏统计数据的小应用程序。 一开始一切顺利,但现在我的 snapshot_details 表包含近 55,000 行,查看 wide_snapshots View 的前 25 行需要 28 秒。

谁能帮我优化这个怪物?

查看 wide_snapshots :

select `s`.`id_snapshot` AS `s_id_snapshot`,
       `s`.`ref_player` AS `s_ref_player`,
       `s`.`ref_world` AS `s_ref_world`,
       `s`.`ref_alliance` AS `s_ref_alliance`,
       `s`.`alliance_role` AS `s_alliance_role`,
       `s`.`classe` AS `s_classe`,
       unix_timestamp(`s`.`timestamp`) AS `s_timestamp`,
       `s`.`rank` AS `s_rank`,
       `s`.`is_substitution` AS `s_is_substitution`,
       `s`.`nb_off_bases` AS `s_nb_off_bases`,
       `s`.`moy_defense` AS `s_moy_defense`,
       `s`.`nb_sup_bases` AS `s_nb_sup_bases`,
       `s`.`moy_sup_lvl` AS `s_moy_sup_lvl`,
       `s`.`moy_def_facility` AS `s_moy_def_facility`,
       `s`.`max_cp` AS `s_max_cp`,
       `s`.`research_points` AS `s_research_points`,
       `s`.`supply_points` AS `s_supply_points`,
       `s`.`command_points` AS `s_command_points`,
       `s`.`credits` AS `s_credits`,
       `s`.`max_repa` AS `s_max_repa`,
       `s`.`nb_bases` AS `s_nb_bases`,
       `s`.`prod_credit` AS `s_prod_credit`,
       `s`.`prod_tiberium` AS `s_prod_tiberium`,
       `s`.`prod_crystal` AS `s_prod_crystal`,
       `s`.`prod_energy` AS `s_prod_energy`,
       `s`.`base1_lvl` AS `s_base1_lvl`,
       `s`.`base1_def` AS `s_base1_def`,
       `s`.`base1_off` AS `s_base1_off`,
       `s`.`base1_prod_energy` AS `s_base1_prod_energy`,
       `s`.`base1_avia_repa` AS `s_base1_avia_repa`,
       `s`.`base1_vehi_repa` AS `s_base1_vehi_repa`,
       `s`.`base1_inf_repa` AS `s_base1_inf_repa`,
       `s`.`base2_lvl` AS `s_base2_lvl`,
       `s`.`base2_def` AS `s_base2_def`,
       `s`.`base2_off` AS `s_base2_off`,
       `s`.`base2_prod_energy` AS `s_base2_prod_energy`,
       `s`.`base2_avia_repa` AS `s_base2_avia_repa`,
       `s`.`base2_vehi_repa` AS `s_base2_vehi_repa`,
       `s`.`base2_inf_repa` AS `s_base2_inf_repa`,
       `sd`.`id_detail` AS `sd_id_detail`,
       `sd`.`ref_snapshot` AS `sd_ref_snapshot`,
       `sd`.`ingame_id` AS `sd_ingame_id`,
       `sd`.`base_id` AS `sd_base_id`,
       `sd`.`base_name` AS `sd_base_name`,
       `sd`.`base_lvl` AS `sd_base_lvl`,
       `sd`.`base_off` AS `sd_base_off`,
       `sd`.`base_def` AS `sd_base_def`,
       `sd`.`construction_yard` AS `sd_construction_yard`,
       `sd`.`defense_facility` AS `sd_defense_facility`,
       `sd`.`defense_hq` AS `sd_defense_hq`,
       `sd`.`command_center` AS `sd_command_center`,
       `sd`.`support_lvl` AS `sd_support_lvl`,
       `sd`.`support_type` AS `sd_support_type`,
       `sd`.`prod_credit` AS `sd_prod_credit`,
       `sd`.`prod_energy` AS `sd_prod_energy`,
       `sd`.`prod_tiberium` AS `sd_prod_tiberium`,
       `sd`.`prod_crystal` AS `sd_prod_crystal`,
       `p`.`ingame_id` AS `p_ingame_id`,
       `p`.`name` AS `p_name`,
       `a`.`ingame_id` AS `a_ingame_id`,
       `a`.`name` AS `a_name`
from (
    (
        (
            `kakawi_basetracker`.`snapshots` `s`
            join `kakawi_basetracker`.`snapshot_details` `sd`
            on `s`.`id_snapshot` = `sd`.`ref_snapshot`
        )
        join `kakawi_basetracker`.`players` `p` 
        on `s`.`ref_player` = `p`.`id_player`
    )
    join `kakawi_basetracker`.`alliances` `a`
    on `s`.`ref_alliance` = `a`.`id_alliance`
)
where (
    `s`.`id_snapshot` = (
        select `s2`.`id_snapshot`
        from `kakawi_basetracker`.`snapshots` `s2`
        where (
            (`s2`.`ref_player` = `s`.`ref_player`) and (`s2`.`ref_world` = `s`.`ref_world`) and (`s2`.`timestamp` > (now() - interval 5 day))
        )
        order by `s2`.`id_snapshot` desc limit 1
    )
)
order by `p`.`name`,`s`.`ref_world`,`sd`.`base_id`

表快照:

CREATE TABLE `snapshots` (
 `id_snapshot` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,
 `ref_player` smallint(5) unsigned zerofill NOT NULL,
 `ref_world` smallint(10) unsigned zerofill NOT NULL,
 `ref_alliance` smallint(10) unsigned zerofill DEFAULT NULL,
 `alliance_role` varchar(32) DEFAULT NULL,
 `classe` enum('GDI','NOD') NOT NULL,
 `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
 `ip_address` varchar(64) NOT NULL DEFAULT '0.0.0.0',
 `rank` smallint(5) unsigned NOT NULL,
 `is_substitution` tinyint(1) NOT NULL,
 `nb_off_bases` tinyint(3) unsigned NOT NULL,
 `moy_defense` decimal(5,2) unsigned NOT NULL,
 `nb_sup_bases` tinyint(3) unsigned NOT NULL,
 `moy_sup_lvl` decimal(5,2) unsigned NOT NULL,
 `moy_def_facility` decimal(5,2) unsigned NOT NULL,
 `max_cp` smallint(5) unsigned NOT NULL,
 `research_points` bigint(20) unsigned NOT NULL,
 `supply_points` smallint(5) unsigned NOT NULL,
 `command_points` mediumint(8) unsigned NOT NULL,
 `credits` bigint(20) unsigned NOT NULL,
 `max_repa` smallint(5) unsigned NOT NULL,
 `nb_bases` tinyint(3) unsigned NOT NULL,
 `prod_credit` bigint(20) unsigned NOT NULL,
 `prod_tiberium` bigint(20) unsigned NOT NULL,
 `prod_crystal` bigint(20) unsigned NOT NULL,
 `prod_energy` bigint(20) unsigned NOT NULL,
 `base1_lvl` decimal(5,2) unsigned NOT NULL,
 `base1_def` decimal(5,2) unsigned NOT NULL,
 `base1_off` decimal(5,2) unsigned NOT NULL,
 `base1_prod_energy` bigint(20) unsigned NOT NULL,
 `base1_avia_repa` mediumint(8) unsigned NOT NULL,
 `base1_vehi_repa` mediumint(8) unsigned NOT NULL,
 `base1_inf_repa` mediumint(8) unsigned NOT NULL,
 `base2_lvl` decimal(5,2) unsigned NOT NULL,
 `base2_def` decimal(5,2) unsigned NOT NULL,
 `base2_off` decimal(5,2) unsigned NOT NULL,
 `base2_prod_energy` bigint(20) unsigned NOT NULL,
 `base2_avia_repa` mediumint(8) unsigned NOT NULL,
 `base2_vehi_repa` mediumint(8) unsigned NOT NULL,
 `base2_inf_repa` mediumint(8) unsigned NOT NULL,
 PRIMARY KEY (`id_snapshot`),
 KEY `snapshots_ref_player_idx` (`ref_player`),
 KEY `snapshots_ref_alliance_idx` (`ref_alliance`),
 KEY `snapshots_ref_world_idx` (`ref_world`),
 KEY `snapshots_ref_player_ref_world_timestamp_idx` (`ref_player`,`ref_world`,`timestamp`),
 CONSTRAINT `snapshots_ref_alliance_fkey` FOREIGN KEY (`ref_alliance`) REFERENCES `alliances` (`id_alliance`) ON DELETE SET NULL ON UPDATE CASCADE,
 CONSTRAINT `snapshots_ref_player_fkey` FOREIGN KEY (`ref_player`) REFERENCES `players` (`id_player`) ON DELETE CASCADE ON UPDATE CASCADE,
 CONSTRAINT `snapshots_ref_world_fkey` FOREIGN KEY (`ref_world`) REFERENCES `worlds` (`id_world`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=12162 DEFAULT CHARSET=utf8

表快照_详细信息:

CREATE TABLE `snapshot_details` (
 `id_detail` bigint(20) unsigned zerofill NOT NULL AUTO_INCREMENT,
 `ref_snapshot` int(10) unsigned zerofill NOT NULL,
 `ingame_id` int(10) unsigned NOT NULL,
 `base_id` tinyint(3) unsigned NOT NULL,
 `base_name` varchar(256) NOT NULL,
 `base_lvl` decimal(5,2) unsigned NOT NULL,
 `base_off` decimal(5,2) unsigned NOT NULL,
 `base_def` decimal(5,2) unsigned NOT NULL,
 `construction_yard` tinyint(3) unsigned NOT NULL,
 `defense_facility` tinyint(3) unsigned NOT NULL,
 `defense_hq` tinyint(3) unsigned NOT NULL,
 `command_center` tinyint(3) unsigned NOT NULL,
 `support_lvl` tinyint(3) unsigned NOT NULL,
 `support_type` enum('Ion','Air','Art') DEFAULT NULL,
 `prod_credit` int(10) unsigned NOT NULL,
 `prod_energy` int(10) unsigned NOT NULL,
 `prod_tiberium` int(10) unsigned NOT NULL,
 `prod_crystal` int(10) unsigned NOT NULL,
 PRIMARY KEY (`id_detail`),
 UNIQUE KEY `snapshot_details_ref_snapshot_base_id_uniq` (`ref_snapshot`,`base_id`) USING BTREE,
 CONSTRAINT `snapshot_details_ref_snapshot_fkey` FOREIGN KEY (`ref_snapshot`) REFERENCES `snapshots` (`id_snapshot`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=61299 DEFAULT CHARSET=utf8

table 上玩家:

CREATE TABLE `players` (
 `id_player` smallint(5) unsigned zerofill NOT NULL AUTO_INCREMENT,
 `ingame_id` int(5) unsigned NOT NULL,
 `name` varchar(64) NOT NULL,
 PRIMARY KEY (`id_player`),
 UNIQUE KEY `players_ingame_id_uniq` (`ingame_id`)
) ENGINE=InnoDB AUTO_INCREMENT=200 DEFAULT CHARSET=utf8

表联盟:

CREATE TABLE `alliances` (
 `id_alliance` smallint(5) unsigned zerofill NOT NULL AUTO_INCREMENT,
 `ingame_id` smallint(5) unsigned NOT NULL,
 `ref_world` smallint(5) unsigned zerofill NOT NULL,
 `name` varchar(64) NOT NULL,
 PRIMARY KEY (`id_alliance`),
 UNIQUE KEY `alliances_ingame_id_ref_world_uniq` (`ingame_id`,`ref_world`) USING BTREE,
 KEY `alliances_ref_world_fkey` (`ref_world`),
 CONSTRAINT `alliances_ref_world_fkey` FOREIGN KEY (`ref_world`) REFERENCES `worlds` (`id_world`)
) ENGINE=InnoDB AUTO_INCREMENT=203 DEFAULT CHARSET=utf8

用原始查询解释:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra   
1   PRIMARY     p   ALL     PRIMARY     NULL    NULL    NULL    197     Using temporary; Using filesort
1   PRIMARY     s   ref     PRIMARY,snapshots_ref_player_idx,snapshots_ref_all...   snapshots_ref_player_idx    2   kakawi_basetracker.p.id_player  27  Using where
1   PRIMARY     a   eq_ref  PRIMARY     PRIMARY     2   kakawi_basetracker.s.ref_alliance   1   
1   PRIMARY     sd  ref     snapshot_details_ref_snapshot_base_id_uniq  snapshot_details_ref_snapshot_base_id_uniq  4   func    2   Using where
2   DEPENDENT SUBQUERY  s2  index   snapshots_ref_player_idx,snapshots_ref_world_idx    PRIMARY     4   NULL    1   Using where

根据user4621032的建议进行解释:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra   
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    899     Using where; Using temporary; Using filesort
1   PRIMARY     a   eq_ref  PRIMARY     PRIMARY     2   s.ref_alliance  1   
1   PRIMARY     p   eq_ref  PRIMARY     PRIMARY     2   s.ref_player    1   
1   PRIMARY     sd  ref     snapshot_details_ref_snapshot_base_id_uniq  snapshot_details_ref_snapshot_base_id_uniq  4   func    2   Using where
3   DEPENDENT SUBQUERY  s2  index   snapshots_ref_player_idx,snapshots_ref_world_idx    PRIMARY     4   NULL    1   Using where
2   DERIVED     snapshots   ALL     NULL    NULL    NULL    NULL    11898   Using where

最佳答案

是这样的: 选择需要记录的最近五天的记录。然后从中选择需要的记录,然后加入需要的表记录。

select
.....
from (
    (
        (
            (select * from `kakawi_basetracker`.`snapshots` where `timestamp` > (now() - interval 5 day)) `s`
            join `kakawi_basetracker`.`snapshot_details` `sd`
            on `s`.`id_snapshot` = `sd`.`ref_snapshot`
        )
        join `kakawi_basetracker`.`players` `p` 
        on `s`.`ref_player` = `p`.`id_player`
    )
    join `kakawi_basetracker`.`alliances` `a`
    on `s`.`ref_alliance` = `a`.`id_alliance`
)
where (
    `s`.`id_snapshot` = (
        select `s2`.`id_snapshot`
        from `kakawi_basetracker`.`snapshots` `s2`
        where (
            (`s2`.`ref_player` = `s`.`ref_player`) and (`s2`.`ref_world` = `s`.`ref_world`)
        )
        order by `s2`.`id_snapshot` desc limit 1
    )
)
order by `p`.`name`,`s`.`ref_world`,`sd`.`base_id`

关于mysql - 优化一个巨大的 MySQL View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32160271/

相关文章:

php - 如何使用mysql从具有父子关系的表中检索数据?

php - 重写基于 php 的自定义电子商务网站的 URL

mysql - 如何连接 5 个不同表中的记录?

sql - ADODB.Recordset 错误 '800a0e78' 对象关闭时不允许操作。经典的ASP

php - 计算 mySQL 中的值

sql - 我应该如何将 DDL 更改从一个环境迁移到另一个环境?

MySQL 从同一组外部表中获取成对的行

java - Hibernate 级联继承

Java与数据库连接

sql - Oracle SQL XML 函数 - 如何在 XML Prolog 中进行编码?