【问题标题】:MySQL index usage query optimizationMySQL索引使用查询优化
【发布时间】:2013-12-08 22:40:44
【问题描述】:

我有以下 MySQL (MyISAM) 表,其中包含大约 300 万行。

CREATE TABLE `tasks` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `node` smallint(6) NOT NULL,
  `pid` int(11) NOT NULL,
  `job` int(11) NOT NULL,
  `a_id` int(11) DEFAULT NULL,
  `user_id` int(11) NOT NULL,
  `state` int(11) NOT NULL,
  `start_time` int(11) NOT NULL,
  `end_time` int(11) NOT NULL,
  `stop_time` int(11) NOT NULL,
  `end_stream` int(11) NOT NULL,
  `message` varchar(255) DEFAULT NULL,
  `rate` float NOT NULL,
  `exiting` int(11) NOT NULL DEFAULT '0',
  `bytes` int(11) NOT NULL,
  `motion` tinyint(4) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `a_id` (`a_id`),
  KEY `job` (`job`),
  KEY `state` (`state`),
  KEY `end_time` (`end_time`),
  KEY `start_time` (`start_time`),
) ENGINE=MyISAM AUTO_INCREMENT=100 DEFAULT CHARSET=utf8;

现在当我运行以下查询时,MySQL 只使用 a_id 索引,需要扫描几千行。

SELECT count(id) AS tries FROM `tasks` WHERE ( job='1' OR job='3' ) 
AND a_id='614' AND state >'80' AND state < '100' AND start_time >='1386538013';

当我添加一个额外的索引 KEY newkey (a_id,state,start_time) 时,MySQL 仍在尝试仅使用 a_id 而不是 newkey。只有在查询中使用提示/强制索引时,它才被使用。更改查询中的字段无济于事。

有什么想法吗?我不一定希望我的陈述中有提示。 MySQL 没有这样做的事实自动向我表明我的表、键或查询在某处存在问题。非常感谢任何帮助。

附加信息:

mysql> show index in tasks;
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| tasks |          0 | PRIMARY   |            1 | id          | A         |     3130554 |     NULL | NULL   |      | BTREE      |         |               |
| tasks |          1 | a_id      |            1 | a_id        | A         |        2992 |     NULL | NULL   | YES  | BTREE      |         |               |
| tasks |          1 | job       |            1 | job         | A         |           5 |     NULL | NULL   |      | BTREE      |         |               |
| tasks |          1 | state     |            1 | state       | A         |           9 |     NULL | NULL   |      | BTREE      |         |               |
| tasks |          1 | end_time  |            1 | end_time    | A         |     1565277 |     NULL | NULL   |      | BTREE      |         |               |
| tasks |          1 | newkey    |            1 | a_id        | A         |        2992 |     NULL | NULL   | YES  | BTREE      |         |               |
| tasks |          1 | newkey    |            2 | state       | A         |        8506 |     NULL | NULL   |      | BTREE      |         |               |
| tasks |          1 | newkey    |            3 | start_time  | A         |     3130554 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

带引号和不带引号的解释:

mysql> DESCRIBE SELECT count(id) AS tries FROM `tasks` WHERE ( job='1' OR job='3' )  AND a_id='614' AND state >'80' AND state < '100' AND start_time >='1386538013';
+----+-------------+-------+------+----------------------------+-----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys              | key       | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+----------------------------+-----------+---------+-------+------+-------------+
|  1 | SIMPLE      | tasks | ref  | a_id,job,state,newkey      | a_id      | 5       | const |  740 | Using where |
+----+-------------+-------+------+----------------------------+-----------+---------+-------+------+-------------+
1 row in set (0.10 sec)

mysql> DESCRIBE SELECT count(id) AS tries FROM `tasks` WHERE ( job=1 OR job=3 )  AND a_id = 614 AND state > 80 AND state < 100 AND start_time >= 1386538013;
+----+-------------+-------+------+----------------------------+-----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys              | key       | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+----------------------------+-----------+---------+-------+------+-------------+
|  1 | SIMPLE      | tasks | ref  | a_id,job,state,newkey      | a_id      | 5       | const |  740 | Using where |
+----+-------------+-------+------+----------------------------+-----------+---------+-------+------+-------------+
1 row in set (0.01 sec)

【问题讨论】:

  • 为什么在选择中使用静态数字作为字符串?这可能会导致查询优化器失效。
  • 感谢您让我知道,不确定这是否有很大不同(不是为了解释),但会尽量避免不必要的引号。
  • 我认为问题在于作业列不在任一索引中。无论它使用哪个索引,它仍然必须引用整个表才能满足 where 子句的那部分。该行查找可能在 IO 中占主导地位,因此索引的选择无关紧要。

标签: mysql indexing


【解决方案1】:

一些事情...我将有一个单一的复合索引 (a_id, job, state, start_time)

这有助于优化所有条件的查询,我认为这是最好的调整顺序。一个“A_ID”,然后是两个工作,一个小的状态范围,然后是基于时间的。接下来,注意没有引号...看来您正在将数字转换为字符串比较,将它们保留为数字进行比较 - 比字符串快。

此外,通过将它们全部作为索引的一部分,它是一个 COVERING 索引,这意味着它不必去原始页面数据获取其他值来测试是否包含合格记录。

SELECT 
      count(*) AS tries 
   FROM 
      tasks
   WHERE 
          a_id = 614
      AND job IN ( 1, 3 ) 
      AND state > 80 AND state < 100 
      AND start_time >= 1386538013;

现在,为什么索引...考虑以下情况。您有两个带有盒子的房间...在第一个房间中,每个盒子都是一个“a_id”,其中是按顺序排列的作业,在每个作业中是状态范围,最后是开始时间。

在另一个房间里,你的盒子按开始时间排序,在那个a_id内排序,最后是状态。

这样更容易找到您需要的东西。这就是你应该如何看待索引。我宁愿为“A_ID = 614”转到一个框,然后跳到作业 1,然后跳到作业 3。在每个作业 1、作业 3 中,抓住 80-100,然后是时间。但是,您更了解每个标准考虑中的数据和数量,并且可以进行调整。

最后,count(ID) 与 count(*)。我关心的只是一个合格的记录。我不需要知道实际的 ID,因为过滤条件已经包含或不包含,为什么要(在这种情况下)寻找实际的“ID”。

【讨论】:

  • 感谢 DRapp,这一切都说得通——这样做让 MySQL 使用 newkey 而不仅仅是 a_id。星号是有意义的,IN 子句也是如此。谢谢你,现在很开心。
  • @user2785818,不客气,作为新手,通常在提供答案时,如果它们有意义/有用,您可以对其进行投票,并选择答案旁边的 CHECKMARK 以便其他人也知道已经解决了。
  • 感谢您让我知道 - 不过投票还需要 4 点经验值 :)
【解决方案2】:

可能mysql认为使用a_id键会使用更少的IO。 可能键 a_id 的基数已经足够好了。 提示/无提示查询说明了什么?

【讨论】:

  • 在原帖中添加了describe语句和索引信息
【解决方案3】:

a_id=614 的大部分状态有 > 80 和

  • INDEX(a_id, start_time, state)
  • INDEX(start_time, a_id, state)

【讨论】:

  • 谢谢,大多数状态都在 80 到 100 之间。是的,选择正确的索引是关键。
猜你喜欢
  • 2012-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-01
  • 2018-11-30
  • 1970-01-01
相关资源
最近更新 更多