MySQL 数据库--SQL语句优化
explain查询和分析sql
开发中,为满足一业务功能,使用mysql书写sql时,一条sql往往有多种写法,那么我们就需要选择执行效率比较高的sql。
因此要比较分析sql的执行过程,且同一条sql我们要比较选择使用最优索引。
通过explain命令可以得到。
先整体了解下SQL查询的基本执行过程:
1)应用通过MySQL API把查询命令发送给MySQL服务器,然后被解析 2)检查权限、MySQL optimizer进行优化,经过解析和优化后的查询命令被编译为CPU可运行的二进制形式的查询计划(query plan),并可以被缓存 3)如果存在索引,那么先扫描索引,如果数据被索引覆盖,那么不需要额外的查找,如果不是,根据索引查找和读取对应的记录 4)如果有关联查询,查询次序是扫描第一张表找到满足条件的记录,按照第一张表和第二张表的关联键值,扫描第二张表查找满足条件的记录,按此顺序循环 5)输出查询结果,并记录binary logs
Explain返回信息:
通过explain可以得到如下结论:
①使用索引比未使用索引,扫描的行数更少查询速度更快;
②在查询语句中使用LIKE关键字进行查询时,如果匹配字符串的第一个字符为“%”时,索引不会被使用。如果“%”不是在第一个位置,索引就会被使用。
③使用多列索引时,只有查询条件中使用了该索引中的第一个索引字段时,索引才会被使用。
注:create index index_age_sex on user(age,sex); age为第一个索引;
④查询语句只有OR关键字时,如果OR前后的两个条件列都是索引时,查询中将使用索引。只要OR前后有一个条件的列不是索引,那么查询中将不使用索引。
注: 1:where 语句里面如果带有or条件, myisam表能用到索引,innodb不行;2:必须所有的or条件都必须是独立索引;
⑤经过普通运算或函数运算后的索引字段不能使用索引。
但是,经过函数运算字段的字段要使用可以使用函数索引,这种需求建议与DBA沟通。
以上结论来自如下测试:
user表: 独立索引:id、name 联合索引:age && sex |
user_noindex表: 无任何索引列; |
CREATE TABLE `user` ( `id` int(11) NOT NULL, `name` varchar(30) NOT NULL, `age` int(11) NOT NULL, `sex` tinyint(4) NOT NULL, `isDeleted` tinyint(4) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `id_unidx` (`id`) USING BTREE, UNIQUE KEY `name_unidx` (`name`) USING BTREE, KEY `index_age_sex` (`age`,`sex`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; |
CREATE TABLE `user_noindex` ( `id` int(11) NOT NULL, `name` varchar(30) NOT NULL, `age` int(11) DEFAULT NULL, `sex` tinyint(4) DEFAULT NULL, `isDeleted` tinyint(4) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
【1】索引对查询的影响-----加索引和不加索引的对比-----使用索引扫描的更少查询更快
语句1:explain SELECT * FROM `user` where name = 'name6';
语句2:explain SELECT * FROM `user_noindex` where name = 'name6';
结果集 | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
语句1 | 1 | SIMPLE | user | const | name_unidx | name_unidx | 32 | const | 1 | null |
语句2 | 1 | SIMPLE | user_noindex | ALL | null | null | null | null | 10 | Using where |
【2】索引对查询的影响-----加索引----使用和未使用索引的对比-----在查询语句中使用LIKE关键字进行查询时,如果匹配字符串的第一个字符为“%”时,索引不会被使用。如果“%”不是在第一个位置,索引就会被使用。
语句1:explain SELECT * FROM `user` where name like '%name6';
语句2:explain SELECT * FROM `user` where name like '%name6%';
语句3:explain SELECT * FROM `user` where name like 'name6%';
结果集 | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
语句1 | 1 | SIMPLE | user | ALL | null | null | null | null | 10 | Using where |
语句2 | 1 | SIMPLE | user | ALL | null | null | null | null | 10 | Using where |
语句3 | 1 | SIMPLE | user | range | name_unidx | name_unidx | 32 | const | 1 | null |
【3】索引对查询的影响-----加索引----使用和未使用索引的对比-----多列索引是在表的多个字段创建一个索引。只有查询条件中使用了这个字段中的第一个字段时,索引才会被使用。
语句1:explain SELECT * FROM `user` where age = '19';
语句2:explain SELECT * FROM `user` where sex = '1';
语句3:explain SELECT * FROM `user` where sex = '1' and age = '19';
结果集 | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
语句1 | 1 | SIMPLE | user | ref | index_age_sex | index_age_sex | 4 | const | 1 | null |
语句2 | 1 | SIMPLE | user | ALL | null | null | null | null | 10 | Using where |
语句3 | 1 | SIMPLE | user | ref | index_age_sex | index_age_sex | 5 | const,const | 1 | null |
【4】索引对查询的影响-----加索引----使用和未使用索引的对比-----查询语句只有OR关键字时,如果OR前后的两个条件的列都是索引时,查询中将使用索引。如果OR前后有一个条件的列不是索引,那么查询中将不使用索引。
语句1:explain SELECT * FROM `user` where (age = '19' OR isDeleted = '0');
语句2:explain SELECT * FROM `user` where (sex = '1' OR age = '19'); -- 联合索引
语句3:explain SELECT * FROM `user` where (name = 'name1' OR id = '1'); -- 独立索引
-- alter table user engine = innodb;
结果集 | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
语句1 | 1 | SIMPLE | user | ALL | index_age_sex | null | null | null | 10 | Using where |
语句2 | 1 | SIMPLE | user | ALL | index_age_sex | null | null | null | 10 | Using where |
语句3 | 1 | SIMPLE | user | ref | PRIMARY,id_unidx,name_unidx | index_age_sex | null | null | 10 | Using where |
-- alter table user engine = myisam;
结果集 | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
语句1 | 1 | SIMPLE | user | ALL | index_age_sex | null | null | null | 10 | Using where |
语句2 | 1 | SIMPLE | user | ALL | index_age_sex | null | null | null | 10 | Using where |
语句3 | 1 | SIMPLE | user | index_merge | PRIMARY, id_unidx, name_unidx |
name_unidx, PRIMARY |
32,4 | null | 2 | Using union (name_unidx,PRIMARY); Using where |
很多查询中需要使用子查询。子查询可以使查询语句很灵活,但子查询的执行效率不高。子查询时,MySQL需要为内层查询语句的查询结果建立一个临时表。然后外层查询语句在临时表中查询记录。查询完毕后,MySQL需要撤销这些临时表。因此,子查询的速度会受到一定的影响。如果查询的数据量比较大,这种影响就会随之增大。在MySQL中可以使用连接查询来替代子查询。连接查询不需要建立临时表,其速度比子查询要快。
limit查询的优化
优化limit查询 .
limit常用于分页处理,时常会伴随order by从句使用,因此大多时候会使用Filesorts这样会造成大量的io问题
select film_id,description from sakila.film order by title limit 50,5; #title列不是索引也不是主键
优化步骤1:使用有索引的列或主键进行order by操作
select film_id ,description from sakila.film order by film_id limit 50,5; #把索引film_id进行order by操作,但仍然扫描了55行。
优化步骤2:记录上次返回的主键,在下次查询时用 主键过滤(避免了数据量大时扫描过多的记录)
select film_id ,description from sakila.film where film_id >55 and film_id<=60 order by film_id limit 1,5 #知道查询的范围为55~60,否则会扫描60行,使用主键过滤where film_id >55 and film_id<=60后只扫描了5行,避免了数据量大时扫描过多的记录。
优化limit查询2。使用这种方式要求主键是按顺序排序,如果中间有断层,则返回的数据行数不符,例如在film_id 55~60中少了一行,则排序是要排5行,则就会出错。