vlambda博客
学习文章列表

MySQL优化查询(二) 索引对查询速度的影响

MySQL中提高性能的一个有效方式就是对数据表设计合理的索引。索引提供了高效访问数据的方法,并且可加快查询的速度,因此,索引对查询的速度有着至关重要的影响。使用索引可以快速地定位表中的某条记录,从而提高数据库查询的速度、提高数据库的性能。

如果查询时没有使用索引,查询语句将扫描表中的所有记录。在数据量大的情况下,这样查询的速度会很慢。如果使用索引进行查询,查询语句可以根据索引快速定位到待查询记录,从而减少查询的记录数,达到提高查询速度的目的。

下面是查询语句中不使用索引和使用索引的对比。首先,分析未使用索引时的查询情况,EXPLAIN语句执行如下:

mysql> explain select * from article where title = 'yellow';+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+| 1 | SIMPLE | article | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 10.00 | Using where |+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+1 row in set, 1 warning (0.00 sec)

可以看到,rows列的值是10,说明“explain select * from article where title = 'yellow'”这个查询语句扫描了表中的10条记录。

然后,在article表的title字段上加上索引,再分析上面的查询语句。执行的EXPLAIN语句及结果如下:

mysql> alter table article add index index_title(`title`);Query OK, 0 rows affected (0.05 sec)Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select * from article where title = 'yellow';+----+-------------+---------+------------+------+----------------+----------------+---------+-------+------+----------+-------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |+----+-------------+---------+------------+------+----------------+----------------+---------+-------+------+----------+-------------+| 1 | SIMPLE | article | NULL | ref | index_title | index_title | 768 | const | 1 | 100.00 | Using index |+----+-------------+---------+------------+------+----------------+----------------+---------+-------+------+----------+-------------+1 row in set, 1 warning (0.00 sec)

结果显示,rows列的值为1,表示这个查询语句只扫描了表中的一条记录,其查询速度自然比扫描10条记录快;而且possible_keys和key的值都是index_title,说明查询时使用了index_title索引。

使用索引查询

索引可以提高查询的速度,但并不是使用带有索引的字段查询时索引都会起作用。本小节将向读者介绍索引的使用。

使用索引有几种特殊情况,在这些情况下有可能使用带有索引的字段查询时索引并没有起作用。下面重点介绍这几种特殊情况。

1.使用LIKE关键字的查询语句

在使用LIKE关键字进行查询的查询语句中,如果匹配字符串的第一个字符为“%”,索引不会起作用。只有“%”不在第一个位置,索引才会起作用。下面将举例说明。

查询语句中使用LIKE关键字,并且匹配的字符串中含有“%”字符,EXPLAIN语句执行如下:

mysql> explain select * from article where title like '%y';+----+-------------+---------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |+----+-------------+---------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+| 1 | SIMPLE | article | NULL | index | NULL | index_title | 768 | NULL | 10 | 11.11 | Using where; Using index |+----+-------------+---------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+1 row in set, 1 warning (0.00 sec)
mysql> explain select * from article where title like 'y%';+----+-------------+---------+------------+-------+----------------+----------------+---------+------+------+----------+--------------------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |+----+-------------+---------+------------+-------+----------------+----------------+---------+------+------+----------+--------------------------+| 1 | SIMPLE | article | NULL | range | index_title | index_title | 768 | NULL | 1 | 100.00 | Using where; Using index |+----+-------------+---------+------------+-------+----------------+----------------+---------+------+------+----------+--------------------------+1 row in set, 1 warning (0.00 sec)

已知title字段上有索引index_title。第1个查询语句执行后,rows列的值为10,表示这次查询过程中扫描了表中所有的10条记录;第2个查询语句执行后,rows列的值为1,表示这次查询过程扫描了1条记录。第1个查询语句中的索引没有起作用,因为第1个查询语句中LIKE关键字后的字符串以“%”开头,而第2个查询语句使用了索引index_title。

2.使用多列索引的查询语句

MySQL可以为多个字段创建索引。一个索引可以包括16个字段。对于多列索引,只有查询条件中使用了这些字段中的第1个字段时索引才会被使用。

【例16.4】在表article表中id和count字段上创建多列索引,验证多列索引的使用情况。

mysql> create index index_id_count on article(`id`, `count`);Query OK, 0 rows affected (0.02 sec)Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select * from article where id = 10;+----+-------------+---------+------------+-------+------------------------+---------+---------+-------+------+----------+-------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |+----+-------------+---------+------------+-------+------------------------+---------+---------+-------+------+----------+-------+| 1 | SIMPLE | article | NULL | const | PRIMARY,index_id_count | PRIMARY | 4 | const | 1 | 100.00 | NULL |+----+-------------+---------+------------+-------+------------------------+---------+---------+-------+------+----------+-------+1 row in set, 1 warning (0.00 sec)
mysql> explain select * from article where count = 10;+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+| 1 | SIMPLE | article | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 10.00 | Using where |+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+1 row in set, 1 warning (0.00 sec)
从第1条查询语句查询结果可以看出,“id= '10'”的记录有1条。第1条语句共扫描了1条记录,并且使用了索引index_id_price。从第2条语句查询结果可以看出,rows列的值是10,说明查询语句共扫描了10条记录,并且key列值为NULL,说明“select * from article where count = 10;”语句并没有使用索引。因为count字段是多列索引的第2个字段,只有查询条件中使用了id字段才会使index_id_count索引起作用。
3.使用OR关键字的查询语句
查询语句的查询条件中只有OR关键字,且OR前后的两个条件中的列都是索引时,查询中才使用索引;否则,查询将不使用索引。
【例16.5】查询语句使用OR关键字的情况:
mysql> explain select * from article where title = 'yellow' or cid = 10\G*************************** 1. row *************************** id: 1 select_type: SIMPLE table: article partitions: NULL type: ALLpossible_keys: index_title key: NULL key_len: NULL ref: NULL rows: 10 filtered: 19.00 Extra: Using where1 row in set, 1 warning (0.00 sec)
mysql> explain select * from article where title = 'yellow' or id = 10\G*************************** 1. row *************************** id: 1 select_type: SIMPLE table: article partitions: NULL type: index_mergepossible_keys: PRIMARY,index_id_count,index_title key: index_title,PRIMARY key_len: 768,4 ref: NULL rows: 2 filtered: 100.00 Extra: Using union(index_title,PRIMARY); Using where1 row in set, 1 warning (0.00 sec)
因为cid字段上没有索引,所以第1条查询语句没有使用索引,总共查询了10条记录;第2条查询语句使用了title和id这两个索引,因为id字段和name字段上都有索引,所以查询的记录数为2条。
优化子查询
MySQL从4.1版本开始支持子查询,使用子查询可以进行SELECT语句的嵌套查询,即一个SELECT查询的结果作为另一个SELECT语句的条件。子查询可以一次性完成很多逻辑上需要多个步骤才能完成的SQL操作。子查询虽然可以使查询语句很灵活,但执行效率不高。执行子查询时,MySQL需要为内层查询语句的查询结果建立一个临时表。然后外层查询语句从临时表中查询记录。查询完毕后,再撤销这些临时表。因此,子查询的速度会受到一定的影响。如果查询的数据量比较大,这种影响就会随之增大。
在MySQL中,可以使用连接(JOIN)查询来替代子查询。连接查询不需要建立临时表,其速度比子查询要快,如果查询中使用索引,性能会更好。连接之所以更有效率,是因为MySQL不需要在内存中创建临时表来完成查询工作。