MySQL如何实现松散的索引扫描

时间:2016-12-25 15:37:24

标签: mysql

最近,我面临一个问题,mysql如何实现松散的索引扫描?

例如:

测试表结构是:


CREATE TABLE test (
  id int(11) NOT NULL default '0',
  v1 int(10) unsigned NOT NULL default '0',
  v2 int(10) unsigned NOT NULL default '0',
  v3 int(10) unsigned NOT NULL default '0',
  PRIMARY KEY  (id),
  KEY v1_v2_v3 (v1,v2,v3)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

select * from test; +----+----+-----+----+ | id | v1 | v2 | v3 | +----+----+-----+----+ | 1 | 1 | 0 | 1 | | 2 | 3 | 1 | 2 | | 10 | 4 | 10 | 10 | | 0 | 4 | 100 | 0 | | 3 | 4 | 100 | 3 | | 5 | 5 | 9 | 5 | | 8 | 7 | 3 | 8 | | 7 | 7 | 4 | 7 | | 30 | 8 | 15 | 30 | +----+----+-----+----+

现在让我们看看两个sql: 第一个:

test
我知道id意味着MySQL使用松散的索引扫描来查询sql.But为什么解释输出列v1是3?我想知道MySQL如何只扫描三行并获得查询结果。

第二个:

v2

上述两个sql之间的唯一区别在于v3,一个是id,另一个是v1_v2_v3。但为什么v1使用松散的索引扫描,v2不要使用松散的索引扫描?我不明白GROUP BY Optimization说:

  

选择列表中使用的唯一聚合函数(如果有)是MIN()和MAX(),并且它们都引用同一列。该列必须位于索引中,并且必须紧跟GROUP BY中的列。

为什么列必须紧跟GROUP BY中的列?

我在网上搜索了很长时间。但没用。请帮助或尝试提供一些如何实现这一点的想法。 谢谢!

2 个答案:

答案 0 :(得分:0)

评论太长了。

基本上,在询问“为什么优化器会以某种方式运行”时,答案是因为设计人员以这种方式实现了它。如果你想知道“为什么”,你就得问问他们。 。 。对于通用论坛来说,这不是一个合适的问题。

但是,我想指出一些事情。如果您认为max(v2)是一个错误,那么您可以在bugs.mysql.com上报告它。我不认为这是一个错误有两个原因:

  1. 文档明确说明优化的工作原理,并且未记录此查询以使用索引(“v2”不遵循group by中的键)。
  2. 即使记录的方式不同,在group by密钥上使用聚合函数也是非理性的。它是有效的SQL,但它只是冗长而且不必要。这些构造在数据库实现者的优先级列表中排在最前面。
  3. 最后,在创建查询计划时,MySQL并没有真正使用统计数据(非常好?)。但是,在大多数数据库中,验证9行(适合单个数据页)上的查询计划通常会导致执行全表扫描和“低效”算法的查询计划。例如,冒泡排序等算法在大量行上效率很低,但它可以是(非常)少量行上最有效的排序算法。

答案 1 :(得分:0)

有没有理由在查询中使用max(v2)?即使您不使用max()函数,结果也是一样的。如果您将查询更改为"请从test中选择v2,其中v1> 3组按v1,v2和#34;,它将通过松散索引扫描方法完成。

以下是列必须紧跟GROUP BY中列的原因。

v1   v2   v3
1     1    1 
1     1    2
1     1    10
1     2    1
1     2    2 
1     2    8  

在这种情况下,通过v1,v2从t1组中选择max(v3)以执行松散索引扫描。这样做如下图所示。

v1   v2   v3
1     1    1  
1     1    2
1     1    10  ------------------> 10 return
1     2    1
1     2    2 
1     2    8   ------------------>  8 return 

但是,如果您通过v1从t1组执行select max(v3),则无法进行松散索引扫描。因为您必须访问所有键才能找到最大值(= 10)。

v1   v2   v3
1     1    1   ------------------> (x) 
1     1    2   ------------------> (x)
1     1    10  ------------------> 10 return
1     2    1   ------------------> (x)
1     2    2   ------------------> (x)
1     2    8   ------------------> (x)

请注意,您可以使用以下命令查看使用松散索引扫描(或紧密索引扫描)访问的记录数。

flush status;

select max(v3) from t1 group by v1,v2;  -- perform loose index scan

show session status like 'Handler_read_key%';


flush status;

select max(v3) from t1 group by v1; -- perform tight index scan

show session status like 'Handler_read_key%';