有没有办法提示mysql使用使用索引进行分组

时间:2013-12-17 20:17:50

标签: mysql group-by query-optimization

我正忙着探索GROUP BY优化。关于经典的“每班最高薪水”查询。并突然奇怪的结果。下面的转储从我的控制台直接。这两个EXPLAINS之间没有发出命令。只过了一段时间。

mysql> explain select name, t1.dep_id, salary 
       from emploee t1
       JOIN ( select dep_id, max(salary) msal 
              from emploee 
              group by dep_id
       ) t2
       ON t1.salary=t2.msal and t1.dep_id = t2.dep_id 
       order by salary desc;
+----+-------------+------------+-------+---------------+--------+---------+-------------------+------+---------------------------------+
| id | select_type | table      | type  | possible_keys | key    | key_len | ref               | rows | Extra    |
+----+-------------+------------+-------+---------------+--------+---------+-------------------+------+---------------------------------+
|  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL   | NULL    | NULL              |    4 | Using temporary; Using filesort |
|  1 | PRIMARY     | t1         | ref   | dep_id        | dep_id | 8       | t2.dep_id,t2.msal |    1 |    |
|  2 | DERIVED     | emploee    | index | NULL          | dep_id | 8       | NULL              |   84 | Using index    |
+----+-------------+------------+-------+---------------+--------+---------+-------------------+------+---------------------------------+
3 rows in set (0.00 sec)

mysql> explain select name, t1.dep_id, salary 
       from emploee t1 
       JOIN (  select dep_id, max(salary) msal 
               from emploee 
               group by dep_id
       ) t2
       ON t1.salary=t2.msal and t1.dep_id = t2.dep_id 
       order by salary desc;
+----+-------------+------------+-------+---------------+--------+---------+-------------------+------+---------------------------------+
| id | select_type | table      | type  | possible_keys | key    | key_len | ref               | rows | Extra    |
+----+-------------+------------+-------+---------------+--------+---------+-------------------+------+---------------------------------+
|  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL   | NULL    | NULL              |    4 | Using temporary; Using filesort |
|  1 | PRIMARY     | t1         | ref   | dep_id        | dep_id | 8       | t2.dep_id,t2.msal |    3 |    |
|  2 | DERIVED     | emploee    | range | NULL          | dep_id | 4       | NULL              |    9 | Using index for group-by    |
+----+-------------+------------+-------+---------------+--------+---------+-------------------+------+---------------------------------+
3 rows in set (0.00 sec)

正如您所注意到的,它在第二次运行中检查的行数减少了十倍。我认为这是因为一些内部计数器发生了变化。但我不想依赖这些柜台。那么 - 有没有办法提示mysql使用“仅使用索引进行分组”行为?

或者 - 如果我的推测是错误的 - 是否有关于行为及其解决方法的其他任何解释?

CREATE TABLE `emploee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `dep_id` int(11) NOT NULL,
  `salary` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `dep_id` (`dep_id`,`salary`)
) ENGINE=InnoDB AUTO_INCREMENT=85 DEFAULT CHARSET=latin1 |
+-----------+
| version() |
+-----------+
| 5.5.19    |
+-----------+

1 个答案:

答案 0 :(得分:3)

嗯,显示索引的基数可能会有所帮助,但请记住:range通常较慢然后index

因为它认为它可以匹配第一个中的完整索引,所以它使用完整索引。在第二个中,它会删除索引并进入一个范围,但是猜测满足较大范围的行总数远低于较小的完整索引,因为所有基数已经改变。将它与此相比:为什么“AA”匹配84行,但“A [任何字符]”仅匹配9(注意它在第一个中使用8个字节的密钥,在第二个中使用4个字节)?第二个实际上不会读取更少的行,EXPLAIN只是在更新了索引的元数据之后才会猜测行数。还不是EXPLAIN 告诉你做什么,但可能会做什么。

更新基数可以或will occur when

  

当打开表时,在SHOW TABLE STATUS和ANALYZE TABLE以及其他情况下(例如表变化太大时),计算表的每个索引中的基数(不同键值的数量)。请注意,如果启用了自动重新哈希设置(默认设置),则当mysql客户端启动时,将打开所有表,并重新估计统计信息。

因此,假设'在任何时候'由于'变化太多',并且是的,与mysql客户端连接可以改变选择服务器索引的行为。另外:在超时计数与连接自动重新散列AFAIK后,重新连接mysql客户端失去连接。如果你想给mysql帮助找到合适的方法,请偶尔运行ANALYZE TABLE,特别是在大量更新之后。如果您认为它所猜测的基数通常是错误的,您可以alter the number of pages读取猜测某些统计信息,但请记住,更高的数字意味着更长时间的基数更新,以及您不希望发生的事情通常在具有大量操作的桌面上“数据已经变为太多”时。

TL; DR :它会以不同方式猜测行,但如果数据可以实现,您实际上更喜欢第一种行为。

添加: 在这个previously linked page上,我们可能也找到了为什么特别dep_id可能会遇到此问题的原因:

  

像1或2这样的小值会导致对基数的估计非常不准确

我可以想象不同dep_id的数量通常非常小,而且我确实在非唯一索引上观察到一个'弹跳'基数,与行数相比,范围相当小我自己的数据库。它很容易猜到数百的1-10的范围,然后下次再次下降,只是基于它选择的特定样本页面。一些试图推断出来的算法。