首先學會如何定位到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強制指定索引。