我正忙着探索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 |
+-----------+
答案 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的范围,然后下次再次下降,只是基于它选择的特定样本页面。一些试图推断出来的算法。