MySQL:索引只用在我select只有主键的时候

MySQL: index used only when I select only primary key

我在 MySQL 中有这个 table:

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

and EXPLAIN EXTENDED SELECT id FROM world.city ORDER BY CountryCode; use index:

| 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

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

没问题。第一个查询运行 faster by not using the index.

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

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

  1. 扫描索引 -- 这会按顺序提供 ids,但不会提供 * 的其余部分。
  2. 对于每个 id,获取其余 * 的数据。这是昂贵的,特别是如果 table 不能被完全缓存。

如果它执行了 "table scan"(如您所见),则代码如下所示:

  1. 将整个 table 读入 tmp table。 (这将是 MEMORYMyISAM,具体取决于几个条件。)
  2. 对 tmp table 进行排序(在磁盘上的 RAM 中)。

table 扫描 + 排序 可能 会更快。

其他说明

如果您的城市在美国,"Los Ranchos de Albuquerque",NM,对于该字段来说太长了。对于世界,请考虑 KZ 中的 87 个字符 "Imeni 50-letiya (Pyat'desyatiletiya) Kazakhskoy Sovetskoy Sotsialisticheskoy Respubliki"。

不使用CHAR可变长度字符串(城市、地区);使用 VARCHAR;它将在 space 中更有效率,因此在速度上。

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