1.SQL語句優化的通常步驟
1).瞭解各類SQL的執行頻率
客戶端鏈接成功後,能夠經過SHOW [SESSION | GLOBAL] STATUS 命令來查看服務器狀態信息;
也能夠在操做系統上使用 mysqladmin extended-status 命令得到這些消息.
SESSION : 表示當前鏈接,做爲默認值
GLOBAL : 自數據庫啓動至今
demo : SHOW STATUS LIKE 'Com_%' ;
Com_xxxx 表示每一個xxx語句執行的次數.咱們一般比較關心的是如下幾個統計參數.
Com_select : 每次查詢累計加1
Com_insert : 執行INSERT的次數, 對於批量插入的INSERT操做,只累加1;
Com_update : 執行UPDATE操做的次數.
Com_delete : 執行DELETE的次數.
上面的參數是全部存儲引擎通用的.
下面這幾個參數只針對InnoDB存儲引擎,累加算法也略有不一樣.
Innodb_rows_read : SELECT 查詢返回的次數
Innodb_rows_inserted : 執行INSERT操做插入的行數.
Innodb_rows_updated : 執行UPDATE操做更新的行數.
Innodb_rows_deleted : 執行DELETE操做刪除的行數.
其中更新操做的計數,是對執行次數的計數,不論提交仍是回滾都會累加.
對於事務型的應用,經過Com_commit 和Com_rollback能夠了解事務提交和回滾的狀況,對於回滾操做很是頻繁的數據庫,可能意味着應用編寫存在問題.
此外,如下幾個參數便於用戶瞭解數據庫的基本狀況.
Connections : 試圖鏈接MySQL服務器的次數.
Uptime : 服務器工做時間.
Slow_queries : 慢查詢次數.
2).定位執行效率較低的SQL語句.
方式1:
經過慢查詢日誌定位執行效率較低的SQL語句.
用--log-slow-queries[=file_name]選項. 啓動時,mysqld寫一個包含全部執行時間超過long_query_time秒的SQL語句日誌文件.
方式2:
使用 SHOW PROCESSLIST 命令查看當前MySQL在進行的線程
包括線程的狀態,是否鎖表等.能夠實時查看SQL的執行狀況,同時對一些鎖表操做進行優化.
3).經過EXPLAIN分析低效SQL的執行計劃
經過EXPLAIN 或 DESC 命令來獲取 MySQL如何執行SELECT 語句的信息.
包括SELECT語句執行過程當中如何鏈接以及鏈接順序.
返回的結果中,有不少信息,包括select_type , table , type , possable_keys , key , key_lenth , rows , Extra 等.
select_type : 表示SELECT的類型.常見的取值有
SIMPLE(不使用錶鏈接/子查詢)
PRIMARY(主查詢,即外層的查詢)
UNION(UNION中的第二個或者後面的查詢語句)
SUBQUERY(子查詢中的第一個SELECT)
table:輸出結果集的表.
type:表示鏈接類型.性能由好到差以下:
system -> const -> eq_ref -> ref -> ref_or_null -> index_merge -> unique_subquery -> index_subquery -> range -> index -> all
system : 表中僅有一行,即常量表.
const : 單表中最多有一個匹配行 , 例如primary key或者unique index
eq_ref : 對於前面的每一行,在此表中只查詢一條記錄.簡單來講,就是多表鏈接使用primary key 或unique index)
ref : 與eq_ref相似,區別在於使用的是普通索引.
ref_or_null : 與ref相似,區別在於包含對NULL的查詢
index_merge : 索引合併優化
unique_subquery : IN的後面是一個查詢主鍵字段的子查詢.
range : 單表中的範圍查詢.
index : 對於前面的每一行,都經過查詢索引來獲得數據.
all : 對於前面的每一行,都經過全表掃描來獲得數據.
possible_keys : 表示查詢時,可能使用的索引.
key : 表示實際使用的索引.
key_len : 索引字段的長度.
rows : 掃描行的數量.
Extra : 執行狀況的說明和描述.
4).肯定問題並採起相應的優化措施
書中例子是a表的type爲all,因此爲WHERE中的列加了個索引,type變爲ref
2.索引問題
1).索引的存儲分類
MyISAM存儲引擎的表,數據和索引時自動分開存儲的,各自是獨立的文件.
InnoDB存儲引擎的表,數據和索引時存儲在同一個表空間裏面,但能夠由多個文件組成.
MySQL中的存儲類型目前只有兩種:BTREE和HASH.
MyISAM和InnoDB都只支持BTREE索引;
MEMORY/HEAP能夠支持HASH以及BTREE索引.
MySQL目前不支持函數索引,可是能對列的前面某一部分進行索引,稱爲前綴索引.
這個特性能夠大大縮小索引文件的大小.
2).如何使用索引
查詢要使用索引最主要的條件是
查詢條件中須要使用索引關鍵字.
若是是多列索引,那麼只有查詢條件使用多列關鍵字最左邊的前綴時,纔可使用索引,不然將不能使用索引.
a.使用索引
(1) 對於建立的多列索引,只要查詢條件中用到了最左邊的列,索引通常就會被使用.
例如對col1,col2(假設col1在前)創建聯合索引,查詢的WHERE條件中沒必要必定要用兩個列都參與篩選,只要最左邊的(這是是col1)參與條件篩選了,就可使用到索引了.這就是索引的前綴特性.
(2) 對於使用LIKE的查詢,後面若是是常量,而且只有%不在第一個字符,索引纔可能會被使用.
另外,若是LIKE後面跟的是一個列的名字,那麼索引也不會被使用.
(3) 若是對大的文本進行搜索,使用全文索引而不使用 LIKE '%...%'
(4) 若是列名是索引,使用column_name is null將使用索引.
b.存在索引,但不使用索引
(1) 若是MySQL估計 使用索引比全表掃描還慢,則不實用索引.
(2) 若是使用 MEMORY/HEAP 表而且WHERE條件中不使用'='進行索引列,那麼不會用到索引. heap表只有在'='條件下才會使用索引.
(3) 用OR 分割開的條件,若是OR前的條件中的列有索引,然後面的列中沒有索引,那麼涉及到的索引都不會被使用.--簡而言之,OR語句中全部條件必須都是用到了索引.
(4) 若是不是索引列的第一部分,複合索引,第一列必須參與條件篩選,纔可能hi用到索引.
(5) LIKE 問題 , 參見a.(2)
(6) 若是列類型字符串,必需要有引號,不然MySQL會認爲是數值型,不實用索引了.
c.查看索引使用狀況
SHOW STATUS LIKE 'Handler_read%';
Handler_read_key表明了一個行被索引值讀的次數.很低的值代表增長索引獲得的性能改善不高,由於該索引並不常用.
Handler_read_rnd_next的值高意味着查詢運行低效,而且應該創建索引補救.
這個值的含義是在數據文件中讀下一行的請求數.若是正在進行大量的表掃描,該值將較高,說明索引不正確或寫入的查詢沒有利用索引.
3).兩個簡單實用的優化方法
a.按期分析表和檢查表.
分析表 :
ANALYZE [LOCAL|NO_WRITE_TO_BINLOG] TABLE tbl_name [,tbl_name ...]
本語句用於分析和存儲 表的關鍵字的分佈,分析的結果將可使得系統獲得準確的統計信息.
在分析期間使用一個讀取鎖定對錶進行鎖定.這對於MyISAM/BDB/InnoDB表有做用.
對於MyISAM表 , 本語句與是一哦那個myisamchk -a至關.
檢查表: CHECK TABLE tbl_name [,tbl_name ...] [option] ... option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED} ;
CHECK TABLE 對MyISAM 和 InnoDB表有做用.對於MyISAM表,關鍵字統計數據被更新.同時CHECK TABLE 也能夠檢查視圖是否有錯誤.
b.按期優化表
OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [,tbl_name ..];
若是已經刪除了表的一大部分,或者若是已經對含有可變長度行的表(含有VARCHAR,BLOB或TEXT列的表)進行了不少更改,
則應使用OPTIMIZE TABLE 命令進行空間碎片整理,實現表優化.
可是OPTIMIZE TABLE只對MyISAM/BDB/InnoDB表起做用.
4).經常使用SQL的優化
a.優化大批量插入數據
當用load命令導入數據的時候,適當設置能夠提升導入的速度.
對於MyISAM存儲引擎的表,能夠經過如下方式快速導入大量數據.
ALTER TABLE tbl_name DISABLE KEYS ;
## here loading the data
LOAD DATA INFILE '/home/mysql/film_test.txt' INTO TABLE film_test ;
ALTER TABLE tbl_name ENABLE KEYS ;
DISABLE/ENABLE KEYS 用來關閉/打開MyISAM表中 非惟一索引 的更新.
對於導入大量數據到一個 空的MyISAM表時,默認就是先導入數據而後才建立索引,因此不用進行設置.
對於InnoDB 表, 能夠有如下幾種方式提升InnoDB表的導入效率.
(1)將導入的數據按照主鍵的順序排列,能夠有效提升導入數據的效率.
(2)在導入數據前執行SET UNIQUE_CHECKS=0 , 即關閉惟一性校驗.
導入結束後 SET UNIQUE_CHECKS=1 恢復惟一性校驗,能夠提升導入效率.
(3)若是應用使用自動提交的方式,建議導入前執行SET AUTOCOMMIT=0,關閉自動提交;導入結束後再執行 SET AUTOCOMMIT = 1 .
b.優化INSERT 語句
(1) 使用INSERT INTO tbl_name VALUES(..),(..),(..)..;
這種比分開執行單個INSERT語句快不少,減小了客戶端與數據庫之間的鏈接/關閉等形式的資源消耗.
(2) 若是從不一樣客戶端插入不少行,經過使用INSERT DELAYED 語句獲得更高的速度.
DELAYED的含義是讓INSERT語句立刻執行,其實數據都被放在內存的隊列中,並無真正寫入磁盤;
LOW_PRIORITY恰好相反,在全部其餘用戶對錶的對鞋完成後,才進行插入.
(3) 利用建表中的選項,將索引文件和數據文件分在不一樣的磁盤上存放.
(4) 若是進行批量插入,能夠增長bulk_insert_buffer_size的閥值.可是隻對MyISAM表起做用.
(5) 從文本文件加載數據,使用LOAD DATA INFILE.一般比使用不少INSERT語句快20倍.
c.優化GROUP BY 語句
若是查詢包括GROUP BY,但用戶想避免排序結果的消耗,可使用 ORDER BY NULL ;
例如 EXPLAIN SELECT id , SUM(money) FROM sale GROUP BY id ORDER BY NULL ; -- 這樣就不使用 filesort 了,而filesort每每很耗時.
d.優化ORDER BY語句
可使用索引代替ORDER BY子句,就不須要額外排序了.
同時知足下面三個條件的SQL語句,可使用索引代替ORDER BY:
WHERE 條件和ORDER BY使用相同的索引
而且 ORDER BY的順序與索引順序相同
而且 ORDER BY的字段 都是升序/降序.
Demo :
SELECT * FROM t ORDER BY key_part1,key_part2 ,.. ;
SELECT * FROM t WHERE key_part1 = 1 ORDER BY key_part1 DESC , key_part2 DESC ;
SELECT * FROM t ORDER BY key_part1 DESC , key_part2 DESC ;
而如下幾種狀況不能使用索引,demo:
# order by的字段混合了 ASC 和DESC
SELECT * FROM t ORDER BY col1 DESC , col2 ASC ;
# 查詢行的關鍵字 與 ORDER BY 中所使用的不相同
SELECT * FROM t WHERE col2 = xx ORDER BY col1 ;
# 對不一樣關鍵字使用ORDER BY
SELECT * FROM t ORDER BY key1 , key2 ;
e.優化嵌套查詢
子查詢使用SELECT語句來建立一個單列的查詢結果,而後把這個結果做爲過濾條件用在另外一個查詢中.
使用子查詢能夠一次性地完成不少邏輯上須要多個步驟才能完成的SQL操做,同時也能夠避免事務或者表鎖死,寫起來也容易.
可是,有些狀況下,子查詢能夠被更有效率的鏈接(JOIN)替代.
例如:
EXPLAIN SELECT * FROM sales WHERE company_id NOT IN (SELECT id FROM company) ;
能夠替換爲
鏈接之因此更高效一些,是由於MySQL不須要在內存中建立臨時表來完成這個邏輯上的須要兩個步驟的查詢工做.
f.優化 OR 條件
增長OR條件的索引.--這 tm 不太好吧
這種方式,對聯合索引不起做用. --也tm 沒給解決方式.
g.使用SQL提示
SQL HINT 是優化數據庫的一個重要手段,就是在SQL語句中加入一些認爲的提示來達到優化操做的目的.
例如: SELECT SQL_BUFFER_RESULTS * FROM ..
這個語句強制MySQL生成一個臨時結果集.結果集生成後,表上的鎖定被釋放.能夠儘快釋放資源.
如下是MySQL中經常使用的一些SQL提示.
(1) USING INDEX
在查詢語句中表名後面,添加USE INDEX 來提供但願MySQL去參考的索引列表,就可讓MySQL再也不考慮使用其餘可用索引.
例如: EXPLAIN SELECT * FROM sales USE INDEX (idx_sales_id) WHERE id = 3 ;
(2) IGNORE INDEX
若是用戶只是想忽略某個或者多個索引則可使用IGNORE INDEX做爲HINT.
例如: EXPLAIN SELECT * FROM sales IGNORE INDEX(idx_sales_id) WHERE id = 3 ;
(3) FORCE INDEX
爲強制MySQL使用一個特定索引.這是MySQL留給用戶的一個自行選擇執行計劃的權利.
例如: EXPLAIN SELECT * FROM sales FROCE INDEX (idx_sales_id) WHERE id > 0 ;