如何在MySQL中正确使用索引

时间:2014-10-13 19:05:13

标签: mysql sql indexing query-optimization explain

我正在运行一个相当简单的自动目录

CREATE TABLE catalog_auto (
    id INT(10) UNSIGNED NOT NULL auto_increment,
    make varchar(35),
    make_t varchar(35),
    model varchar(40),
    model_t varchar(40),
    model_year SMALLINT(4) UNSIGNED,
    fuel varchar(35),
    gearbox varchar(15),
    wd varchar(5),
    engine_cc SMALLINT(4) UNSIGNED,
    variant varchar(40),
    body varchar(30),
    power_ps SMALLINT(4) UNSIGNED,
    power_kw SMALLINT(4) UNSIGNED,
    power_hp SMALLINT(4) UNSIGNED,
    max_rpm SMALLINT(5) UNSIGNED,
    torque SMALLINT(5) UNSIGNED,
    top_spd SMALLINT(5) UNSIGNED,
    seats TINYINT(2) UNSIGNED,
    doors TINYINT(1) UNSIGNED,
    weight_kg SMALLINT(5) UNSIGNED,
    lkm_def TINYINT(3) UNSIGNED,
    lkm_mix TINYINT(3) UNSIGNED,
    lkm_urb TINYINT(3) UNSIGNED,
    tank_cap TINYINT(3) UNSIGNED,
    co2 SMALLINT(5) UNSIGNED,
    PRIMARY KEY(id),
    INDEX `gi`(`make`,`model`,`model_year`,`fuel`,`gearbox`,`wd`,`engine_cc`),
    INDEX `mkt`(`make`,`make_t`),
    INDEX `mdt`(`make`,`model`,`model_t`)
);

到目前为止,该表有大约60.000行,因此,即使没有索引,也没有简单的查询无法处理。

关键是,我试图摆脱使用索引的问题,所以我根据最常见的查询做了一些。

我希望engine_cc能够获得一组特定的标准:

SELECT DISTINCT engine_cc FROM catalog_auto WHERE make='audi' AND model='a4' and model_year=2006 AND fuel='diesel' AND gearbox='manual' AND wd='front';

EXPLAIN说:

+----+-------------+--------------+------+---------------+------+---------+-------------------------------------+------+--------------------------+
| id | select_type | table        | type | possible_keys | key  | key_len | ref                                 | rows | Extra                    |
+----+-------------+--------------+------+---------------+------+---------+-------------------------------------+------+--------------------------+
|  1 | SIMPLE      | catalog_auto | ref  | gi,mkt,mdt    | gi   | 408     | const,const,const,const,const,const |    8 | Using where; Using index |
+----+-------------+--------------+------+---------------+------+---------+-------------------------------------+------+--------------------------+

查询正在按预期使用gi索引,这里没问题。

选择基本标准后,我还需要其余的列:

SELECT * FROM catalog_auto WHERE make='audi' AND model='a4' and model_year=2006 AND fuel='diesel' AND gearbox='manual' AND wd='front' AND engine_cc=1968;

EXPLAIN说:

+----+-------------+--------------+------+---------------+------+---------+-------------------------------------------+------+-------------+
| id | select_type | table        | type | possible_keys | key  | key_len | ref                                       | rows | Extra       |
+----+-------------+--------------+------+---------------+------+---------+-------------------------------------------+------+-------------+
|  1 | SIMPLE      | catalog_auto | ref  | gi,mkt,mdt    | gi   | 411     | const,const,const,const,const,const,const |    3 | Using where |
+----+-------------+--------------+------+---------------+------+---------+-------------------------------------------+------+-------------+

它选择了一个KEY,但没有使用索引。然而,查询非常快(集合中的1行(0.00秒)),但由于表没有那么多行,我假设即使没有索引,它也是相同的。

尝试这样:

SELECT * FROM catalog_auto WHERE id IN (SELECT id FROM catalog_auto WHERE make='audi' AND model='a6' AND model_year=2009);

再次,在EXPLAIN

+----+--------------------+--------------+-----------------+--------------------+---------+---------+------+-------+-------------+
| id | select_type        | table        | type            | possible_keys      | key     | key_len | ref  | rows  | Extra       |
+----+--------------------+--------------+-----------------+--------------------+---------+---------+------+-------+-------------+
|  1 | PRIMARY            | catalog_auto | ALL             | NULL               | NULL    | NULL    | NULL | 59060 | Using where |
|  2 | DEPENDENT SUBQUERY | catalog_auto | unique_subquery | PRIMARY,gi,mkt,mdt | PRIMARY | 4       | func |     1 | Using where |
+----+--------------------+--------------+-----------------+--------------------+---------+---------+------+-------+-------------+

仍未使用任何索引,甚至不使用PRIMARY KEY。不应该这样,至少使用PRIMARY KEY?

文档说:MySQL可以忽略一个密钥,即使它找到一个密钥,如果它确定全表扫描会更快,这取决于查询。

这就是它没有使用任何索引的原因吗?这是一个好习惯吗?如果没有,在给定上述查询的情况下,您如何建议为SELECT *语句索引列始终使用索引。

我不是一个MySQL专家,所以任何指针都会非常感激。

将MySQL 5.5与InnoDB一起使用。

2 个答案:

答案 0 :(得分:3)

我不是MySQL专家,但我的猜测是索引用于行查找,但实际数据必须从数据页中检索,所以另外一个查找是必要的。

在您的第一个查询中,您可以通过在索引键处查找 来获取您要求的数据。当您要求第二个和第三个查询中的索引中没有列时,引擎会使用该键在数据表上执行SEEK,因此它仍然非常快。

使用SQL性能,因为优化器有很多自由选择"最佳"计划,在索引时,证据就在布丁中。如果添加索引使得常见查询更快,更好,则使用它。如果没有,那么节省维护索引的空间和开销(或寻找更好的索引)。

请注意,您没有获得免费午餐 - 其他索引实际上可以减慢系统,特别是如果您经常插入或更新已建立索引的列,因为系统将会必须不断维持这些指数。

答案 1 :(得分:2)

我基本上是在说@DStanley所说的相同答案,但我想扩展它,而不是在评论中。

“使用索引”注释表示查询仅使用 索引来获取所需的列。
缺少此注释并不意味着查询未使用索引。

您应该查看的是EXPLAIN报告中的key列:

+----+-------------+--------------+------+---------------+------+---------+-------------------------------------------+------+-------------+
| id | select_type | table        | type | possible_keys | key  | key_len | ref                                       | rows | Extra       |
+----+-------------+--------------+------+---------------+------+---------+-------------------------------------------+------+-------------+
|  1 | SIMPLE      | catalog_auto | ref  | gi,mkt,mdt    | gi   | 411     | const,const,const,const,const,const,const |    3 | Using where |
+----+-------------+--------------+------+---------------+------+---------+-------------------------------------------+------+-------------+

key列表示优化程序选择使用gi索引。所以它 使用索引。并且ref列确认引用该索引的所有七列。

它必须获取更多列才能返回*,这意味着它无法声明“使用[only] index”。

另请阅读https://dev.mysql.com/doc/refman/5.6/en/explain-output.html的摘录:

  
      
  • 使用索引

         

    仅使用索引树中的信息从表中检索列信息,而无需执行额外的搜索来读取实际行。当查询仅使用属于单个索引的列时,可以使用此策略。

  •   

我想到了这个类比,电话簿:

如果你在电话簿中查找某个公司,它的效率很高,因为该书按名称按字母顺序排列。当您找到它时,您也可以在同一条目中找到电话号码。所以如果这就是你所需要的,那就非常快。这是仅索引查询。

如果您想了解有关商家的额外信息,例如他们的营业时间或凭据,或者他们是否携带某种产品,您必须采取额外步骤使用该电话号码来打电话询问。获得这些信息还需要几分钟的时间。但你仍然能够找到电话号码而无需阅读整本电话簿,所以至少它不需要花费数小时或数天。这是一个使用索引的查询,但还必须从表中查找行以获取其他数据。