一、各種SQL執行效率
# 查看當前session中所統計參數的值:
Show status like ‘Com_%’;
Show status like ‘Innodb%’;
Show status like ‘Connections%’;
Show status like ‘Uptime%’;
Show status like ‘Slow_queries%’;
二、定位執行效率低的SQL語句
- 通過查詢慢日志定位
- Show processlist 命令查看當前MySQL在進行的線程。
三、通過Explan分析低效SQL的執行計划
通過 explain 或者 desc 命令獲取MySQL如何執行select 語句的信息。
參數說明:
- select_type:表示select的類型,常見取值simple(簡單表,即不使用表連接或者子查詢)、primary(主查詢,即外層的查詢)、union(union中的第二個或者后面的查詢語句)、subquery(子查詢中的第一個select)等
- table:輸出結果集的表
- type:表示MySQL在表中找到所需行的方式,或者叫訪問類型,常見:all,index,range,ref,eq_ref,[const,system],null;從左到右,性能最差到最好。
All: 全盤掃描
Index:索引全掃描
Range:索引范圍掃描,常見 < , > , <= , >= , between 等操作符
Ref:使用非唯一索引掃描或唯一索引的前綴掃描,返回匹配某個單獨值的記錄行
Eq_ref:唯一索引,簡單說,就是多表連接中使用primary key 或者 unique index作為關聯條件
Const/System:單表中最多有一行匹配行,如primary key 或 unique index
Null:不用訪問表或者索引,直接獲得結果
4.possible_keys:表示查詢時可能使用的索引。
5.key:表示實際使用的索引
6.key_len: 顯示MySQL決定使用的鍵長度。可以反映出一個多重主鍵里MySQL實際使用了哪部分。
7.ref: 顯示那個字段或常熟與key一起被使用。
8.rows:掃描行的數量
9.Extra:執行情況的說明和描述,包含不適合在其他列中顯示但是對執行計划非常重要的額外信息。Only index 信息只能用索引樹中的信息檢索;where used 表示使用了 where限制,但是用索引還不夠;impossible where 表示通過收集到的同級信息判斷出不可能存在的結果。還有可能的值 Using filesort、using temporary、select tables optimized way。
四、通過show profile分析SQL
# 查看是否支持profile
默認 profiling是關閉的,通過set 語句在 session 級別開啟 profiling
Show profiles; 和 show profile [for query query_id];不加默認是最后一個id
注意:sending data狀態表示MySQL線程開始訪問數據行並把結果返回給客戶端,而不僅僅是返回結果給客戶端。由於在sending data 狀態下,MySQL線程往往需要做大量的磁盤讀取操作,所以經常是整個查詢中耗時最長的狀態。
進一步支持all,cpu,block io,context,switch,page faults等明細類型來查看MySQL在使用什么資源上耗費了過高的時間。
五、通過trace分析優化器如何選擇執行計划
使用方式:首先打開trace,設置格式為json,設置trace最大能夠使用的內存大小,避免解析過程中因為默認內存過小而不能夠完整顯示。
Set optimizer_trace = ‘enabled=on’ , end_markers_in_json=on;
Set optimizer_trace_max_men_size = 1000000;
接着執行想做trace的sql語句
最后檢查 information_schema.optimizer_trace 知道MySQL如何執行sql
六、簡單實用的優化方法
1.定期分析表和檢查表
# 分析表語句
Analyze [local | no_write_to_binlog] table table_name [, table_name]…
# 檢查表語句
Check table table_name [,table_name]…[option]…option = {QUICK|FAST|MEDIUM|EXTENDEN|CHANGED}
檢查一個或多個表是否有錯誤。對myisam 和 Innodb表有作用。也可以用於視圖
2.定期優化表
Optimize [local | no_write_to_binlog] table table_name [,table_name] …
如果已經刪除了表的一大部分,或者如果已經對含有可變長度行的表(含有varchar、blob或text列的表)進行了很多改動,則要用optimize table 命令進行表優化。 可將表中的空間碎片進行合並,並且可以刪除由於刪除或者更新造成的空間浪費,但次命令只對myisam 、bdb和Innodb表起作用。
注意analyze、check、optimize、alter table 執行期間將對表進行鎖定,在數據庫不繁忙的時候執行。
七、簡單SQL的優化
1.大批量插入數據
Alter table table_name disable keys;
Load data infile “ xxx.txt” into table table_name;
Alter table table_name enable keys;
說明:
打開或者關閉MyISAM表非唯一索引的更新。如導入一個非空的MyISAM表示,設置這兩個命令可以提高導入效率。對於空的表,默認就是先導入數據后創建索引的。
提高innodb的導入速度:
1.對於innodb此方法無效,因為innodb類型的表默認是按照主鍵的順序保存的,所以導入的數據按照主鍵的順序排列,可以有效的提高導入效率。
2.導入前執行 set unique_checks = 0, 關閉唯一性校驗,導入結束后,set unique_checks = 1,恢復唯一性校驗;
3.如果應用是自動提交,導入前set autocommit=0 ,導入后 set autocommit=1,相對快一些。
2.優化insert語句
如果從同一客戶端插入很多行,盡量使用多個值表的insert語句,大大縮減客戶端與數據庫之間的鏈接、關閉等消耗,比分開執行的單個insert語句塊,大部分情況下,快上好幾倍。
Insert into table_name values (xxx),(xxxx),(xxxxx)…
如果從不同客戶端插入很多行,使用insert delayed 語句得到更高的速度。 Delayed的含義是讓insert語句馬上執行,其實數據都被放在內存的隊列中,並沒有真正寫入磁盤,這比每條語句分別插入要快的多。Low_priority剛好相反,在所有其他用戶對表的讀寫完成后再進行插入。
將索引文件和數據文件分在不同的磁盤上存放(利用建表中的選項)
如果進行批量插入,可以通過增加bulk_insert_buffer_size 變量值的方法來提高速度,但是只對MyISAM使用。
3.優化order by 語句
- 1. 通過有序索引順序掃描直接返回有序數據
- 2. 通過對返回數據進行排序,filesort排序(不是通過索引直接返回順序結果的排序)
所以,盡量減少額外的排序,通過索引直接返回有序數據。Where和order by 使用相同的索引,並且order by 的順序和索引順序相同,並且order by 的字段都是升序或者都是降序。否則可定需要額外的排序操作。
Filesort的優化:
針對filesort排序,mysql有兩種排序算法:
通過max_length_for_sort_data的大小和query語句取出的字段總大小判斷使用哪一種算法,如果max_length_for_sort_data更大,使用第二種算法。
Filesort排序盡量只使用必要的字段,select具體的字段名,不要用*,可以減少排序區的使用,提高sql性能。
4.優化group by語句
默認情況下,mysql對group by col1,col2.。。的字段進行排序。與在查詢中指定order by col1,col2.。。類似。因此如果顯示包含一個order by 子句,則對mysql的實際執行性能沒有什么影響。
如果查詢包括group by 但用戶要避免排序結果的消耗,可以指定order by null 禁止排序。
5.優化嵌套查詢
子查詢可以使用一個select語句來創建一個單列的查詢結果,然后把這個結果作為過濾條件用在另一個查詢中。
有些情況下子查詢可以被更有效率的鏈接join替代,因為mysql不需要再內存中創建臨時表來完成這個邏輯上需要多個步驟的查詢工作。
6.優化 or 條件
含有or條件的查詢子句,如果要利用索引,則or之間的每個條件列都必須用到索引,如果沒有索引,則應該考慮增加索引。
MySQL在處理含有or字句的查詢時,實際是對or的各個字段分別查詢后的結果進行union操作。
7.優化分頁查詢
Limit 1000,20 語句,mysql排序出前1020條記錄后僅僅返回需要的第1001到1020條的記錄,前1000條記錄會被拋棄,查詢和排序的代價非常高。
a) 第一種優化思路:
在索引排序完成排序分頁的操作,最后根據主鍵關聯回原表查詢所需要的其他列內容。
b) 把limit查詢轉換成某個位置的查詢
例如,第41頁最后一行的rental_id = 15640
如上,把limit m,n 轉換成 limit n的查詢,注意:只適合排序字段不會出現重復值的特定環境。
8.使用sql提示
簡單來說就是在sql語句中加入一些人為的提示來達到優化操作的目的。
常見的sql提示:
a) Use index (index_name)
放在表名的后面,提供希望mysql去參考的索引列表,不在考慮其他可用的索引。
b)Ignore index (index_name)
忽略一個或者多個索引
c) Force index (index_name)
強制讓mysql使用一個特定的索引
常用sql技巧
- 正則表達式的使用
Mysql利用regexp命令提供給用戶擴展的正則表達式功能。
Where email regexp “@163[,.]com$”
- 巧用rand()提取隨機行
Order by rand() 完成隨機抽取某些行,即把數據隨機排序
- 利用group by 的 with rollup子句
with rollup子句可以檢索出更多的分組聚合信息,對聚合分類后的結果再進行匯總。也就是說一個group by 語句執行完成后可以滿足用戶想要得到的任何一個分組以及分組組合的聚合信息值。
注意:Order by 和 with rollup相互排斥。Limit 用在 With rollup 后面。
- 用bit group functions做統計
Bit_add()、bit_or()函數
- 數據庫名、表名大小寫問題
操作視同的大小寫的敏感性決定了數據庫名和表名對大小寫的敏感性。
列、索引、存儲子程序和觸發器名在任何平台上對大小寫不敏感。
- 使用外鍵需要注意的問題
除innodb存儲引擎,其他用創建外鍵語句沒有效果,只最為備忘錄或者注釋來提醒用戶目前正定義的列指向另一個表中的一列。
八、優化數據庫對象
- 優化表的數據類型
Procedure analyze()對當前應用的表進行分析,對數據表中的列的數據類型提出優化建議。
出現枚舉,是因為分析的數據太少,因此函數覺得我們使用枚舉類型會更合理;如果是對一個大表進行分析會更准確。
不要為那些值多於16個或者256字節的enum類型提出建議。
- 通過拆分(數據表拆分)提高表的訪問效率
MyISAM有兩種拆分:
a) 垂直拆分
把主碼和一些列放到另一個表,然后把主碼和另外的列放到另一個表。
使用於:某些列常用,而另外一些列不常用。
注意:查詢所有需要聯合(join)
b) 水平拆分
根據一列或多列數據的值把數據行放到兩個獨立的表中。
查詢所有用 (union)
- 逆規范化
增加冗余列、增加派生列、重組表、分割表
- 使用中間表提高統計查詢速度
即 創建一個和原表數據結構一樣的表,把要統計的數據插入到中間表,在中間表中進行統計。且可以在中間表中靈活的增加索引或增加臨時用的新字段。