有没有办法提示 mysql 使用 Using index for group-by

Posted

技术标签:

【中文标题】有没有办法提示 mysql 使用 Using index for group-by【英文标题】:Is there a way to hint mysql to use Using index for group-by 【发布时间】:2013-12-17 20:17:49 【问题描述】:

我正忙于探索 GROUP BY 优化。在经典的“每个部门的最高工资”查询中。突然奇怪的结果。下面的转储从我的控制台直接。这两个解释之间没有发出命令。只过了一段时间。

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】:

嗯,显示索引的基数可能会有所帮助,但请记住:range 通常然后indexes 在那里。

因为它认为它可以匹配第一个索引中的完整索引,所以它使用完整索引。在第二个中,它删除索引并进入一个范围,但猜测满足 larger 范围的总行数大大低于 smaller 完整索引,因为所有基数发生了变化。比较一下:为什么 "AA" 匹配 84 行,但 "A[any character]" 只匹配 9 (注意它在第一个中使用 8 个字节的键,在第二个中使用 4 个字节)?第二个实际上不会读取更少的行,EXPLAIN 只是在更新它的索引元数据后以不同的方式猜测行数。 EXPLAIN 也不是告诉你一个查询做什么,而是它可能会做什么。

更新基数可以或will occur when:

表的每个索引中的基数(不同键值的数量)是在打开表时计算的,在 SHOW TABLE STATUS 和 ANALYZE TABLE 以及其他情况下(例如当表更改太多时)。请注意,如果 auto-rehash 设置设置为 on(默认值),则在 mysql 客户端启动时会打开所有表并重新估计统计信息。

因此,假设“在任何时候”由于“变化太多”,是的,连接mysql 客户端可以改变选择服务器索引的行为。另外:在超时后失去连接后重新连接 mysql 客户端算作使用自动重新哈希 AFAIK 连接。如果您想帮助 mysql 找到正确的方法,请偶尔运行ANALYZE TABLE,尤其是在大量更新之后。如果您认为它猜测的基数通常是错误的,您可以 alter the number of pages 它读取以猜测一些统计信息,但请记住,数字越大意味着该基数的运行更新时间越长,并且您不希望经常发生的事情当有很多操作的表上的“数据变化很大”时。

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

添加: 在这个previously linked page 上,我们可能也可以找到为什么特别是dep_id 可能有这个问题:

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

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

【讨论】:

以上是关于有没有办法提示 mysql 使用 Using index for group-by的主要内容,如果未能解决你的问题,请参考以下文章

Mysql登录出错 user root@localhost(using password:NO)

phpMyAdmin提示“Access denied for user 'root'@'localhost' (using password: NO)”的解决办法(代码

有没有办法在 Redshift Spectrum 中使用“IN”条件检查多个列?

MySQL5.7出现Your password has expired. To log in you must change it using a client that supports expir

pip安装出现Fatal error in launcher: Unable to create process using '"'的解决办法

iOS之报错“Cannot create __weak reference in file using manual reference counting”解决办法