【问题标题】:MySQL How can I speed up this SELECT query?MySQL 如何加快这个 SELECT 查询?
【发布时间】:2014-09-30 20:14:50
【问题描述】:

我有一个包含超过 900 万行的非常大的表,在我的软件中,我需要在循环中快速对其执行选择查询。问题是查询需要将近 4 秒才能完成。这是其中一个查询(它们都相似,因为它们都有相同的 WHERE 子句,这会减慢它们的速度:

SELECT MIN(id)
FROM `04c1Tg0M`
WHERE `tried` = 0;

我将尝试过的列用作布尔值。该值是 1 或 0。这是该查询的解释:

--------+--------------------------+
| id | select_type | table    | type  | possible_keys | key  | key_len | ref  | rows    | Extra                    |
+----+-------------+----------+-------+---------------+------+---------+------+---------+--------------------------+
|  1 | SIMPLE      | 04c1Tg0M | index | NULL          | pdex | 158     | NULL | 9275107 | Using where; Using index |
+----+-------------+----------+-------+---------------+------+---------+------+---------+--------------------------+

这是表结构:

CREATE TABLE `04c1Tg0M` (
    `id` int(20) NOT NULL AUTO_INCREMENT,
    `username` varchar(50) NOT NULL,
    `tried` tinyint(1) DEFAULT '0',
    PRIMARY KEY (`id`),
    KEY `pdex` (`username`,`id`,`tried`)
) ENGINE=MyISAM AUTO_INCREMENT=9275108 DEFAULT CHARSET=utf8

这是显示索引的输出:

| Table    | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| 04c1Tg0M |          0 | PRIMARY  |            1 | id          | A         |     9275107 |     NULL | NULL   |      | BTREE      |         |
| 04c1Tg0M |          1 | pdex     |            1 | username    | A         |     9275107 |     NULL | NULL   |      | BTREE      |         |
| 04c1Tg0M |          1 | pdex     |            2 | id          | A         |     9275107 |     NULL | NULL   |      | BTREE      |         |
| 04c1Tg0M |          1 | pdex     |            3 | tried       | A         |     9275107 |     NULL | NULL   | YES  | BTREE      |         |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

以下是相关查询的输出:

+---------+
| MIN(id) |
+---------+
|       1 |
+---------+
1 row in set (3.76 sec)

我需要大幅减少查询时间。非常感谢任何帮助。

【问题讨论】:

  • 你能发布EXPLAIN SELECT的输出吗?你知道tried = 0tried = 1 占多少行?

标签: mysql optimization select


【解决方案1】:

您应该在(tried, id) 上添加一个索引。

您已经在(username,id,tried) 上添加了一个索引,但是该索引不能有效地用于您编写的查询,因为字段tried 不是索引中的第一项。


来自手册页How MySQL Uses Indexes

MySQL 为这些操作使用索引:

  • 查找特定索引列key_colMIN()MAX() 值。这是由预处理器优化的,该预处理器检查您是否在索引中key_col 之前出现的所有关键部分上使用WHERE key_part_N = constant。在这种情况下,MySQL 为每个 MIN()MAX() 表达式执行单个键查找,并将其替换为常量。如果所有表达式都替换为常量,则查询立即返回。例如:

    选择 MIN(key_part2),MAX(key_part2) FROM tbl_name WHERE key_part1=10;

请特别注意,WHERE 子句必须引用出现在您希望计算MINMAX 的列之前的列,这就是您当前索引的原因没有得到有效利用。


我需要快速循环对其进行选择查询

您确定需要循环查询吗?您是否考虑过将循环移入数据库?也许您真正需要的是加入?发送一个获取大量数据的单个查询比发送许多只获取少量数据的小查询要好。

【讨论】:

  • 我刚刚将 show index 的输出添加到我的帖子中。你能看看有没有什么不对劲的地方吗?另外,您建议创建第二个复合索引对吗?
  • 是的,索引是改进和加速 mysql 查询操作的最佳选择。
  • @xendi:你没有说另一个索引是干什么用的。
  • 我也有这个:SELECT COUNT(id) FROM 04c1Tg0M WHERE tried = 0;
  • @xendi:除了计算行数之外,COUNT ... WHERE 没有特别的优化,这需要O(n) 时间。在tried 上添加索引可能会有所帮助,但查询仍然需要与计数成正比的时间。假设 tried = 0tried = 1 之间的比例为 50/50,这只会给你 50% 的加速。你能考虑一个完全不同的方法吗?也许您可以缓存结果而不是多次请求它?
【解决方案2】:

tried 列上添加索引应该可以加快您的查询速度。 MySQL 为这些操作使用索引:

查找特定索引列 key_col 的 MIN() 或 MAX() 值。这是由预处理器优化的,该预处理器检查您是否在索引中 key_col 之前出现的所有关键部分上使用 WHERE key_part_N = constant。在这种情况下,MySQL 为每个 MIN() 或 MAX() 表达式执行单个键查找,并将其替换为常量。如果所有表达式都替换为常量,则查询立即返回。例如:

SELECT MIN(key_part2),MAX(key_part2) FROM tbl_name WHERE key_part1=10;

【讨论】:

    【解决方案3】:

    如果您在tried 列上添加索引,您的查询应该会加快速度。

    你可以使用类似的命令

    create index tried_ix1 on 04c1Tg0M (tried);
    

    附带说明,如果您在 InnoDb 表上应用索引,还请考虑该索引是否应该被聚集。例如,如果您要以特定的(可能已排序的)顺序访问大量表记录,可能是一份报告,那么请考虑根据该报告的排序要求创建一个聚集索引。

    【讨论】:

      猜你喜欢
      • 2017-12-23
      • 1970-01-01
      • 2012-12-01
      • 1970-01-01
      • 2017-01-12
      • 1970-01-01
      • 2014-05-06
      • 2016-01-08
      • 1970-01-01
      相关资源
      最近更新 更多