首先学会如何定位到SQL语句mysql
在MySQL中能够经过命令查看服务器该表状态信息sql
show status like 'Com_______';
若是想查看整个数据库信息数据库
show global status like 'Com_______';
下面这些对于全部存储引擎的表操做都会进行累计json
有专门针对Innodb统计的,其中 rows_read
表明的是读取的行数。服务器
show status like 'Innodb_rows_%';
对于事务型的应用,经过 Com_commit 和 Com_rollback 能够了解事务提交和回滚的状况, 对于回滚操做很是频繁的数据库,可能意味着应用编写存在问题。session
经过下面命令能够查看MySQL进程mysql优化
找到相应的SQL语句以后,能够EXPLALIN获取MySQL的执行信息。app
其中每一个列的解释:ide
id:id相同表示加载表的执行顺序从上到下,id越大加载的优先级越高性能
select_type:表示 SELECT 的类型,常见的取值有
table:输出结果集的表
type:表示表的链接类型,性能好到坏的结果
possible_keys:表示查询时,可能使用的索引。
key:表示实际使用的索引
key_len:索引字段的长度
rows:扫描行的数量
Extra:执行状况的说明和描述
根据以上内容建立 Teacher
、 Student
表,经过ClassID关联
create table Teacher ( teacherId int not NULL AUTO_INCREMENT, teacherName VARCHAR(50), ClassID int, primary key (teacherId) ) ENGINE =innodb DEFAULT charset=utf8; create table Student ( StudentID int not NULL AUTO_INCREMENT, ClassId int, StudentName varchar(50), primary key (StudentID) ) ENGINE = INNODB DEFAULT charset=utf8; INSERT into Teacher(teacherName,ClassID) values("小李",204),("小刘",205),("小杨",206); INSERT into Student(ClassId,StudentName) VALUES(204,"张三"),(205,"李四"),(206,"王五");
(1)、Id相同表示执行顺序从上到下
EXPLAIN select * from Teacher t,Student s where t.ClassID=s.ClassID;
(2)、Id不一样表示,Id越大越先执行
explain select *from Teacher where ClassId =( select ClassId from Student where StudentName='张三');
(3)、Id有相同的也有不一样的,先执行Id大的,再从上到下执行。
(1)、SIMLPLE简单的select查询,不包含子查询或者UNION
explain select * from Teacher;
(2)、PRIMARY查询当中包含了子查询,最外层就是改查询的标记
(3)、SUBQUERY在select或者Where中包含了子查询
explain select *from Teacher where ClassId=(select ClassId from Student where StudentId=1);
(4)、DERIVED在form列表包含子查询
explain select * from (select * from Student where Student.StudentID>2 ) a where a.ClassID=204;
若是查询显示都是SIMLPLE是由于mysql5.7对 derived_merge 参数默认设置为on,也就是开启状态,咱们在mysql5.7中把它关闭 shut downn 使用以下命令就能够了
set session optimizer_switch=`derived_merge=off`; set global optimizer_switch=`derived_merge=off`;
(5)、UNION 、UNION RESULT
explain select * from Student where StudentID=1 union select * from Student where StudentID=2;
UNION指的是后面那个Select,UNION RESULT 将前面的select语句和后面的select联合起来。
(1)、NULL直接返回结果,不访问任何表索引
select NOW();
(2)、system查询结果只有一条的数据,const类型的特例
explain select * from (select * from Student where StudentID=1) a;
(3)、const根据主键或者惟一索引进行查询,表示一次就找到了
EXPLAIN select * from Student where StudentID=1;
(4)、eq_ref 索引是主键或者惟一索引,使用多表关联查询查询出来的数据只有一条
explain select * from Student s,Teacher t where s.StudentID=t.teacherId
(5)、ref 根据非惟一性的索引查询,返回的记录有多条,好比给某个字段添加索引
explain select * from Student s WHERE StudentName='张三1';
(6)、range 范围查询 between <> in等操做,前提是用索引,要本身设定索引字段;
explain select * from Student where StudentID in (2,3);
(7)、index 遍历整个索引树,至关于查询了整张表的索引
explain select StudentID from Student;
(8)、ALL 遍历全部数据文件
explain select * from Student;
经过这个Type就能够判断当前查询返回了多少行,有没有走索引仍是走全表扫描
结果从最好到最坏
NULL > system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL system > const > eq_ref > ref > range > index > ALL
(1)possible_keys:可能用到的索引
(2)key:实际用到的索引
(3)key_len:key的长度,越短越好
sql语句执行扫描的行数
(1)using filesort :会对进行文件排序即内容,而不是按索引排序,效率慢
EXPLAIN select *from Student order by StudentName;
若是要优化的话能够对该字段建索引
(2)using index 根据根据索引直接查,避免访问表的数据行
explain select StudentID from Student order by StudentID ;
(3)using temporary 使用临时表保存结果,在没有索引的状况下,须要进行优化
EXPLAIN select * from Teacher t GROUP BY teacherName;
报错:Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'demo_01.Teacher.teacherName' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
解决办法:
一、找到mysql的配置文件 my.ini (通常在mysql根目录)
二、在my.cn中将如下内容添加到 [mysqld]下
个人是:etc/my.cnf
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
show profile能够分析sql运行的时间,经过 have_profiling
能够查看MySQL是否支持profile
默认profiling是关闭的,能够经过语句打开
set profiling=1;//打开
执行SQL语句以后乐意经过show profiles指令,来查看语句的耗时
show profiles;
能够经过Show profile for query Query_id查看每一个阶段的耗时
Show profile for query 2;
其中Sending data表示来讲访问数据库并把结果返回给数据库的过程,MySQL须要作大量的磁盘读取操做,所以是最耗时的。
在知道最消耗时间的状态后,能够选择all、cpu、block to、context switch、page fault等明细查看在什么资源上浪费了时间
show profile cpu for query 2;
Mysql有一个优化器按照规则对SQL进行优化处理,trace就是用来分析优化器的执行计划
首先开启trace开关,而后设置trace文件占用的内存空间
set optimizer_trace="enabled=on",end_markers_in_json=on; set optimizer_trace_max_mem_size=1000000;
执行SQL语句以后检查系统表就能够知道如何执行的SQL
select * from information_schema.optimizer_trace\G;
*************************** 1. row *************************** QUERY: select * from Student where StudentId<1 TRACE: { "steps": [ { "join_preparation": { "select#": 1, "steps": [ { "expanded_query": "/* select#1 */ select `Student`.`StudentID` AS `StudentID`,`Student`.`ClassId` AS `ClassId`,`Student`.`StudentName` AS `StudentName` from `Student` where (`Student`.`StudentID` < 1)" //把*查询的都解析出来了 } ] /* steps */ } /* join_preparation */ }, { "join_optimization": { "select#": 1, "steps": [ { "condition_processing": { "condition": "WHERE", "original_condition": "(`Student`.`StudentID` < 1)", "steps": [ { "transformation": "equality_propagation", "resulting_condition": "(`Student`.`StudentID` < 1)" }, { "transformation": "constant_propagation", "resulting_condition": "(`Student`.`StudentID` < 1)" }, { "transformation": "trivial_condition_removal", "resulting_condition": "(`Student`.`StudentID` < 1)" } ] /* steps */ } /* condition_processing */ }, { "substitute_generated_columns": { } /* substitute_generated_columns */ }, { "table_dependencies": [ { "table": "`Student`", "row_may_be_null": false, "map_bit": 0, "depends_on_map_bits": [ ] /* depends_on_map_bits */ } ] /* table_dependencies */ }, { "ref_optimizer_key_uses": [ ] /* ref_optimizer_key_uses */ }, { "rows_estimation": [ { "table": "`Student`", "range_analysis": { "table_scan": { "rows": 4, "cost": 3.9 } /* table_scan */, "potential_range_indexes": [ { "index": "PRIMARY", "usable": true, "key_parts": [ "StudentID" ] /* key_parts */ }, { "index": "index_id_Student", "usable": true, "key_parts": [ "StudentID" ] /* key_parts */ }, { "index": "index_Name_Student", "usable": false, "cause": "not_applicable" } ] /* potential_range_indexes */, "setup_range_conditions": [ ] /* setup_range_conditions */, "group_index_range": { "chosen": false, "cause": "not_group_by_or_distinct" } /* group_index_range */, "analyzing_range_alternatives": { "range_scan_alternatives": [ { "index": "PRIMARY", "ranges": [ "StudentID < 1" ] /* ranges */, "index_dives_for_eq_ranges": true, "rowid_ordered": true, "using_mrr": false, "index_only": false, "rows": 1, "cost": 1.21, "chosen": true }, { "index": "index_id_Student", "ranges": [ "StudentID < 1" ] /* ranges */, "index_dives_for_eq_ranges": true, "rowid_ordered": false, "using_mrr": false, "index_only": false, "rows": 1, "cost": 2.21, "chosen": false, "cause": "cost" } ] /* range_scan_alternatives */, "analyzing_roworder_intersect": { "usable": false, "cause": "too_few_roworder_scans" } /* analyzing_roworder_intersect */ } /* analyzing_range_alternatives */, "chosen_range_access_summary": { "range_access_plan": { "type": "range_scan", "index": "PRIMARY", "rows": 1, "ranges": [ "StudentID < 1" ] /* ranges */ } /* range_access_plan */, "rows_for_plan": 1, "cost_for_plan": 1.21, "chosen": true } /* chosen_range_access_summary */ } /* range_analysis */ } ] /* rows_estimation */ }, { "considered_execution_plans": [ { "plan_prefix": [ ] /* plan_prefix */, "table": "`Student`", "best_access_path": { "considered_access_paths": [ { "rows_to_scan": 1, "access_type": "range", "range_details": { "used_index": "PRIMARY" } /* range_details */, "resulting_rows": 1, "cost": 1.41, "chosen": true } ] /* considered_access_paths */ } /* best_access_path */, "condition_filtering_pct": 100, "rows_for_plan": 1, "cost_for_plan": 1.41, "chosen": true } ] /* considered_execution_plans */ }, { "attaching_conditions_to_tables": { "original_condition": "(`Student`.`StudentID` < 1)", "attached_conditions_computation": [ ] /* attached_conditions_computation */, "attached_conditions_summary": [ { "table": "`Student`", "attached": "(`Student`.`StudentID` < 1)" } ] /* attached_conditions_summary */ } /* attaching_conditions_to_tables */ }, { "refine_plan": [ { "table": "`Student`" } ] /* refine_plan */ } ] /* steps */ } /* join_optimization */ }, { "join_execution": { "select#": 1, "steps": [ ] /* steps */ } /* join_execution */ } ] /* steps */ }
(1)索引对查询效率的提高
根据有索引的ID和名字查询结果,数据量不是很大只有两万可能不是很明显,有索引的快一些
若是查询的条件值没有索引,能够经过建立索引来达到快速查询的目的
(2)全值匹配,先建立联合索引,全部列都指定具体值
create index idx_Stuname_id on Student(ClassId,StudentName); explain select * from Student where StudentName='货物9000号' and ClassId=9000;
(3)最左前缀法则,从最左边一个 索引开始匹配,顺序位置不受where
影响,法则是查询的结果包含索引的最左列,且后面没有跳过其余列。
explain select * from Student where StudentName='货物9000号' and ClassId=9000;
若是将where后面最左列匹配的索引ClassId
增长一个其余字段就没法用到idx_Stuname_id
索引
explain select * from Student where ClassId=9000 and StudentID=20771 AND StudentName=20771;
走索引就至关于爬楼梯,从一层一层开始爬,一层爬完爬二层,不能直接从二层开始爬,也不能爬了二层开始爬第三层
(3)在范围查询的字段后面索引失效
explain select *from Student where 索引1= and 字段>2 and 索引2=
所以索引2将会失效,用不到该索引
(4)若是对某一个列进行了计算操做,索引失效
explain select * from Student where ClassId BETWEEN 20771 and 20111
(5)、若是字符串不加单引号,索引会失效。
(6)、使用覆盖索引(只访问索引的查询),避免使用select *
在查询的时候将*号改为须要查询的字段或者索引,减小没必要要的开销,使用索引查询,using index condition
会将须要的字段查询出来
using index :使用覆盖索引的时候就会出现 using where:在查找使用索引的状况下,须要回表去查询所需的数据 using index condition:查找使用了索引,可是须要回表查询数据 using index ; using where:查找使用了索引,可是须要的数据都在索引列中能找到,因此不须要回表查询数据
(7)、若是有 or
后面的字段没有索引,则整个索引失效
explain select * from Teacher where teacherId=2;
本来主键索引
加上or以后,索引失效
explain select * from Teacher where ClassId=204 or teacherId=2;
(8)、以like '%XX'开头不走索引
正常走索引
explain select * from Student where StudentName LIKE '货物9000号%';
在like前加上%号
explain select * from Student where StudentName LIKE '%货物9000号%' ;
不走索引解决办法:使用覆盖索引,将*号改为有索引的列,再经过索引查询
explain select StudentID from Student where StudentName LIKE '%货物9000号%'
(8)若是再一张表中,一个字段数据基本全是1,只有为2。这时候给该字段创建索引,查询1的时候mysql认为走全表速度更快就不会走索引,若是查询2就会走索引。
(9)IS NUL、IS NOT NULL有时走索引
若是一个字段中全部数据都不为空,那么查询该字段时会走索引,是少许的就会走索引,大多数不会走索引。
EXPLAIN select * from Student where StudentName is NULL; EXPLAIN select * from Student where StudentName is NOT NULL;
(10)in走索引、not in 不走索引,但也不是绝对的,按照第八条
(11)单列索引和复合索引
create index idx_Stuname_id on Student(ClassId,StudentName); 就至关于建立了三个索引 : ClassId StudentName ClassId + StudentName
若是建立单个索引,数据库不会所有使用,而是选择一个最优的。通常选择辨识度最高的。
(12)查看全部使用状况
show status like 'Handler_read%'; show global status like 'Handler_read%';//全局
Handler_read_first:索引中第一条被读的次数。若是较高,表示服务器正执行大量全索引扫描(这个值越低越好)。 Handler_read_key:若是索引正在工做,这个值表明一个行被索引值读的次数,若是值越低,表示索引获得的性能改善不高,由于索引不常用(这个值越高越好)。 Handler_read_next :按照键顺序读下一行的请求数。若是你用范围约束或若是执行索引扫描来查询索引列,该值增长。 Handler_read_prev:按照键顺序读前一行的请求数。该读方法主要用于优化ORDER BY ... DESC。 Handler_read_rnd :根据固定位置读一行的请求数。若是你正执行大量查询并须要对结果进行排序该值较高。你可能使用了大量须要MySQL扫描整个表的查询或你的链接没有正确使用键。这个值较高,意味着运行效率低,应该创建索引来补救。 Handler_read_rnd_next:在数据文件中读下一行的请求数。若是你正进行大量的表扫描,该值较高。一般说明你的表索引不正确或写入的查询没有利用索引。
(1)大批量插入数据时,须要将主键按顺序插入会快不少
(2)若是插入过程当中有惟一索引,能够先关闭索引检查,防止每插入一条时对索引进行筛查
set unique_checks=1;//1为打开 0为关闭
(3)手动提交事务,关闭自动提交事务
set autocommit=1;//1为打开 0为关闭
(1)将多条insert语句改成一条
(2)手动开启事务,所有插入以后,再提交
(3)尽可能按主键顺序插入
(1)若是按照多字段排序,要么统一升序要么统一降序
(2)order 不用后面的字段须要和索引的顺序保持一致
(3)若是Extra列还出现Using filesort,表示进行了额外的一次排序,考虑使用联合索引
(1)使用Group by若是Extra列出现Using filesort,表示Group by语句默认进行了排序,可使用Order by null取消排序
(2)使用Group by若是Extra列出现Using Temporary,能够给字段创建索引提升效率
(1)把多表链接查询替换为子查询
(1)若是须要用到索引,则每一个列须要单首创建索引,不能用复合索引
(2)使用Union替换Or
(1)根据主键进行排序分页操做,获得主键再回原表进行查询
(2)主键自增时,能够直接根据ID查询,数据没删除的状况下
(1)USE index,在有多个索引的状况下,但愿Mysql使用该索引,但不是必定会用。
explain select * from sales2 use index (ind_sales2_id) where id = 3
(2)ignore index能够忽略使用该索引,使用其余索引
(3)在数据量不少的状况下,查询数据占很大比重,即便使用了索引,数据库也不会用,这时候使用force index强制指定索引。