MySQL:索引仅在我选择主键时使用

时间:2016-02-22 18:13:42

标签: mysql sql query-optimization database-performance

我在MySQL中有这个表:

Table: city
Columns: 
  ID int(11) AI PK 
  Name char(35) 
  CountryCode char(3) 
  District char(20) 
  Population int(11)

我想在CountryCode订购时使用索引,所以我在这个列上创建了索引:

Index: CountryCode
Definition:
  Type BTREE 
  Unique No 
  Columns CountryCode

为什么 EXPLAIN EXTENDED SELECT * FROM city ORDER BY CountryCode; 不使用索引:

| id: 1 
| select type: SIMPLE 
| table: city 
| partitions: NULL
| type: ALL 
| possible_keys: NULL
| key: NULL
| key_len: NULL
| ref: NULL 
| rows: 4188 
| filtered: 100.00 
| Extra: Using filesort

EXPLAIN EXTENDED SELECT id FROM world.city ORDER BY CountryCode; 使用索引:

| id: 1
| select type: SIMPLE
| table: city
| partitions: NULL
| type: index
| possible_keys: NULL
| key: CountryCode
| key_len: 3
| ref: null
| rows: 4188
| filtered: 100.00
| Extra: Using index

如何更改此行为 - 即在第一个示例中添加索引用法?

1 个答案:

答案 0 :(得分:3)

不是问题。第一个查询使用索引而不是更快运行。

首先,让我们剖析第二个查询。显然你正在使用InnoDB。这意味着INDEX(country_code)确实是INDEX(country_code, id)。也就是说,PRIMARY KEY被隐式地添加到任何二级密钥上。第二个查询只需要country_codeid,因此可以完全在索引中执行,如Using index所示。它按顺序读取索引并传递结果。很有效率。

现在,让我们看看第一个查询。这次您要求*,而不仅仅是id。其余字段不在索引中。如果要使用索引(并且您可以使用... FROM city FORCE INDEX(CountryCode) ...进行测试),则必须执行此操作:

  1. 浏览索引 - 这会按顺序提供ids,但不会提供*的其余部分。
  2. 对于每个ID,请访问其余*的数据。这是昂贵的,特别是如果表不能完全缓存。
  3. 相反,如果它执行了“表扫描”(这是您所看到的),代码的工作方式如下:

    1. 将整个表读入tmp表。 (根据几个标准,这将是MEMORYMyISAM。)
    2. 对tmp表进行排序(在磁盘上的RAM中)。
    3. 表扫描+排序可能更快。

      其他说明

      如果你的城市在美国,那么新墨西哥州的“Los Ranchos de Albuquerque”对于这个领域来说太长了。对于这个世界,考虑在KZ的87-char“Imeni 50-letiya(Pyat'desyatiletiya)Kazakhskoy Sovetskoy Sotsialisticheskoy Respubliki”。

      不要将CHAR用于可变长度字符串(城市,地区);使用VARCHAR;它在空间上会更有效率,因此速度更快。

      使用CHAR(3)作为固定长度的country_code,但请务必指定CHARACTER SET ascii。如果使用默认值utf8,则需要9个字节,而不是3个字节。