在哪些字段上建立索引?查询的字段 (WHERE) 或返回的字段 (SELECT)

dot*_*hen 0 mysql optimization explain

我在AWS RDS 中型实例上有一个大型 MySQL 数据库表(约 100 万行并且还在增长):

mysql> describe clients;
+-----------------+---------------+------+-----+---------+----------------+
| Field           | Type          | Null | Key | Default | Extra          |
+-----------------+---------------+------+-----+---------+----------------+
| id              | int(11)       | NO   | PRI | NULL    | auto_increment |
| name            | varchar(500)  | YES  |     | NULL    |                |
| address         | varchar(500)  | YES  |     | NULL    |                |
| city            | varchar(200)  | YES  |     | NULL    |                |
| state           | varchar(100)  | YES  |     | NULL    |                |
| zip             | varchar(50)   | YES  |     | NULL    |                |
| country         | varchar(50)   | YES  |     | NULL    |                |
| phone           | varchar(20)   | YES  | UNI | NULL    |                |
| source          | varchar(20)   | YES  | MUL | NULL    |                |
| campaign        | varchar(200)  | YES  |     | NULL    |                |
| search_term     | varchar(200)  | YES  |     | NULL    |                |
| search_location | varchar(200)  | YES  |     | NULL    |                |
| added           | datetime      | YES  |     | NULL    |                |
| email           | varchar(150)  | YES  |     | NULL    |                |
| website         | varchar(150)  | YES  |     | NULL    |                |
| full_output     | varchar(5000) | YES  |     | NULL    |                |
| client          | varchar(50)   | YES  |     | NULL    |                |
| is_deleted      | int(2)        | YES  |     | 0       |                |
| is_valid        | int(2)        | YES  |     | 1       |                |
+-----------------+---------------+------+-----+---------+----------------+
19 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

我经常需要执行以下查询的变体:

SELECT name, zip FROM clients WHERE source IN ('Foo','foo','Bar','bar') AND added>'2013-11-25 13:00:00' limit 150000, 150000;
Run Code Online (Sandbox Code Playgroud)

和相关的EXPLAIN

mysql> EXPLAIN SELECT name, zip FROM clients WHERE source IN ('Foo','foo','Bar','bar') AND added>'2013-11-25 13:00:00' limit 150000, 150000;
+----+-------------+------------+-------+---------------+--------+---------+------+---------+-------------+
| id | select_type | table      | type  | possible_keys | key    | key_len | ref  | rows    | Extra       |
+----+-------------+------------+-------+---------------+--------+---------+------+---------+-------------+
|  1 | SIMPLE      | clients    | range | source        | source | 63      | NULL | 1168144 | Using where |
+----+-------------+------------+-------+---------------+--------+---------+------+---------+-------------+
1 row in set (0.03 sec)
Run Code Online (Sandbox Code Playgroud)

我应该做哪些优化?我应该在nameandzip字段上添加索引,还是在addedandsource字段上添加索引?

Mic*_*bot 5

接受的答案忽略了覆盖索引的概念,也没有提到索引在多个列上的重要性,一起在一个索引中。

WHERE子句中两列的单个索引:

ALTER TABLE clients ADD KEY(source,added) -- adding this 
ALTER TABLE clients ADD KEY(added,source) -- or this
Run Code Online (Sandbox Code Playgroud)

...通常比每列上的单个索引对您的帮助更多,因为否则优化器可能只选择使用两列之一。上述哪个索引更有用取决于“源”和“添加”中值的分布。用于查询的选定索引将出现在EXPLAIN输出的“key”中。“使用 where”通常意味着,在选择的查询计划将导致获取的行中,服务器意识到其中一些仍然不符合选择标准,并且必须随后由服务器过滤(如在例如,可能需要过滤大量数字,因为没有使用索引)。

一个覆盖索引可能是特别有价值的也是,因为违背了断言,“行内的字段是快速和容易使发动机得到的,”他们只是更快,比发现通过扫描整个表中的行更容易-他们仍然需要时间,并消耗资源。

这就是覆盖索引的用武之地。添加带有 (source, added,zip,name) 的索引可能会大大提高您的性能,因为一旦服务器通过使用索引找到相关行,它就不需要查找其余数据,因为数据实际上在索引内。当使用覆盖索引时,explain 的“key”列将包含正在使用的索引的名称,“Extra”列将包含“using index”(意思是,使用索引实际检索数据,而不仅仅是找到它。)

因此,虽然您确实根据选择标准编制了索引,但这并不是故事的全部。

另请注意,无论索引什么,索引都将仅用于从索引中最左边的列开始的实际搜索,直到遇到不在WHERE子句中的列。

因此,(source, added) 上的索引可以优化在WHERE子句中同时包含“源”和“添加”的查询的行的查找......或者子句中只有“源” WHERE,但该索引不会用于在 where 子句中仅“添加”的查找,因为其左侧有一列未使用。类似地,(source, added,zip,name) 上的单个索引可以优化查询的查询,其中WHERE子句提到 source ... 或 source and added ... 或 source and added and zip ... 或 source and added and zip and名称......但不仅仅是“zip”......不仅仅是“名称”......不是“添加”和“名称”和“zip”......你明白了。一个索引是不相关的,开始于,在,WHERE

请注意,您在 where 子句中列出事物的顺序没有区别,只要所有条件都是AND。这是您在网上会发现的一种误解。任何等效的表达式都被优化器理解为等效的。

此外,除非您明确禁用它,否则 IN('Foo','foo') 是多余的,因为由于collat​​ions选择将不区分大小写,因此 'foo' 应该足以找到任何大写排列。