MySQL COUNT查询执行时间太长

Sur*_*dhu 3 mysql performance count mysql-5.5 query-performance

我有以下查询需要 7 秒才能运行

SELECT COUNT(*) AS `count` 
FROM `yuldi`.`businesses` AS `Business` 
  LEFT JOIN `yuldi`.`businesses_categories` AS `BusinessesCategory` 
    ON (`Business`.`id` = `BusinessesCategory`.`business_id`) 
  LEFT JOIN `yuldi`.`categories` AS `Category` 
    ON (`BusinessesCategory`.`category_id` = `Category`.`id`) 
WHERE `Category`.`slug` = 'building-construction'
Run Code Online (Sandbox Code Playgroud)

当我第二次运行它时需要 160 毫秒。为什么每次第一次运行都需要这么长时间

解释:

| id | select_type | table              | type   | possible_keys                                                                                   | key                                          | key_len | ref      | rows  | Extra                    |
|  1 | SIMPLE      | Category           | const  | PRIMARY,UNIQUE_SLUG,index_lug                                                                   | UNIQUE_SLUG                                  | 302     | const    |     1 | Using index              |
|  1 | SIMPLE      | BusinessesCategory | ref    | PRIMARY,fk_businesses_has_categories_categories1_idx,fk_businesses_has_categorie_businesses_idx | fk_businesses_has_categories_categories1_idx | 4       | const    | 49630 | Using where; Using index |
|  1 | SIMPLE      | Business           | eq_ref | PRIMARY                                                                                         | PRIMARY                                      | 4       | yuldi.BusinessesCategory.business_id |     1 | Using index              |
Run Code Online (Sandbox Code Playgroud)

Rol*_*DBA 6

需要返工的三个方面

方面#1:查询

请看查询

SELECT COUNT(*) AS `count`
FROM `yuldi`.`businesses` AS `Business`
LEFT JOIN `yuldi`.`businesses_categories` AS `BusinessesCategory`
    ON (`Business`.`id` = `BusinessesCategory`.`business_id`)
LEFT JOIN `yuldi`.`categories` AS `Category`
    ON (`BusinessesCategory`.`category_id` = `Category`.`id`)
WHERE `Category`.`slug` = 'building-construction';
Run Code Online (Sandbox Code Playgroud)

您正在执行 LEFT JOIN。这不是进行 COUNT 所必需的。为什么 ?

如果是统计商家数量,只需要从一张表开始数

SELECT COUNT(*) AS `count`
FROM `yuldi`.`businesses` AS `Business`;
Run Code Online (Sandbox Code Playgroud)

由于您在 上有 WHERE 子句Category.slug,因此需要 JOIN。您应该从 LEFT JOIN 切换到 INNER JOIN。这将产生一个较小的内部临时表。将在临时表中搜索 slug 'building-construction'。

方面#2:索引

从 EXPLAIN 计划中,我看到两个名称相似的索引

  • fk_businesses_has_categories_categories1_idx
  • fk_businesses_has_categories_businesses_idx

您需要查看它们的列并确保列列表不同。

您可能想要制作两个索引

ALTER TABLE BusinessesCategory
    ADD INDEX bus_cat_ndx (business_id,category_id),
    ADD INDEX cat_bus_ndx (category_id,business_id)
;
Run Code Online (Sandbox Code Playgroud)

拥有复合索引可能意味着更少地通过表来筛选 JOIN 信息。

请记住,这个建议是盲目的,因为我不知道您数据的关键分布。

方面#3:缓存

收集索引统计信息是为了查询评估不会立即转化为慢查询。这种缓慢表现在真正的罪魁祸首:缓存。您的查询第二次运行得更快的原因与您第一次读取的数据有关。当您发出查询时,您需要的数据很可能在 MySQL 的缓存中不可用。

MySQL 的两个主要缓存是 InnoDB 缓冲池(用于访问 InnoDB 表的查询)和 MyISAM 键缓存(用于访问 MyISAM 表的查询)。

InnoDB 缓存数据和索引页,而 MyISAM 只缓存索引页。

当检索数据进行比较时,mysqld 会首先尝试确定它需要的数据是否在 RAM 中。有服务器状态变量可以监控:

InnoDB 缓存

MyISAM 缓存

  • Key_read_requests:从 MyISAM 密钥缓存中读取密钥块的请求数。
  • Key_reads:从磁盘物理读取密钥块到 MyISAM 密钥缓存的次数。如果 Key_reads 很大,那么您的 key_buffer_size 值可能太小了。缓存未命中率可以计算为 Key_reads/Key_read_requests。

如果您的查询第一次很慢,这仅意味着您查询所需的数据不在缓存中。这将是由你的任何指示Innodb_buffer_pool_readsKey_reads递增。

第二次,同一查询的数据和/或索引页将在 RAM 中,并且更多地可用于您的查询和来自其他人的查询。

我建议您查看 InnoDB Buffer Pool 和/或 MyISAM key cache 的大小