mysql數據庫是如今應用最普遍的數據庫系統。與數據庫打交道是每一個Java程序員平常工做之一,索引優化是必備的技能之一。mysql
案例一:大學有段時間學習爬蟲,爬取了知乎300w用戶答題數據,存儲到mysql數據中。那時不瞭解索引,一條簡單的「根據用戶名搜索所有回答的sql「須要執行半分鐘左右,徹底知足不了正常的使用。程序員
案例二:最近線上應用的數據庫頻頻出現多條慢sql風險提示,而工做以來,對數據庫優化方面所知甚少。例如一個用戶數據頁面須要執行不少次數據庫查詢,性能很慢,經過增長超時時間勉強能夠訪問,可是性能上須要優化。算法
合適的索引,能夠大大減少mysql服務器掃描的數據量,避免內存排序和臨時表,提升應用程序的查詢性能。sql
mysql數據中有多種索引類型,primary key,unique,normal,但底層存儲的數據結構都是BTREE;有些存儲引擎還提供hash索引,全文索引。數據庫
BTREE是最多見的優化要面對的索引結構,都是基於BTREE的討論。服務器
查詢數據最簡單暴力的方式是遍歷全部記錄;若是數據不重複,就能夠經過組織成一顆排序二叉樹,經過二分查找算法來查詢,大大提升查詢性能。而BTREE是一種更強大的排序樹,支持多個分支,高度更低,數據的插入、刪除、更新更快。數據結構
現代數據庫的索引文件和文件系統的文件塊都被組織成BTREE。性能
btree的每一個節點都包含有key,data和只想子節點指針。學習
btree有度的概念d>=1。假設btree的度爲d,則每一個內部節點能夠有n=[d+1,2d+1)個key,n+1個子節點指針。樹的最大高度爲h=Logb[(N+1)/2]。優化
索引和文件系統中,B-TREE的節點常設計成接近一個內存頁大小(也是磁盤扇區大小),且樹的度很是大。這樣磁盤I/O的次數,就等於樹的高度h。假設b=100,一百萬個節點的樹,h將只有3層。即,只有3次磁盤I/O就能夠查找完畢,性能很是高。
創建索引後,合適的查詢語句才能最大發揮索引的優點。
另外,因爲查詢優化器能夠解析客戶端的sql語句,會調整sql的查詢語句的條件順序去匹配最合適的索引。
-- 表建立語句 CREATE TABLE people ( last_name VARCHAR(20) NOT NULL, first_name VARCHAR(20) NOT NULL, gender CHAR(1) NOT NULL,
birth date NOT NULL, KEY last_first_name_gender_key(last_name, first_name, gener) );
查詢語句where條件和索引中的全部列進行匹配。
1 SELECT * FROM people WHERE last_name='zhang' AND first_name='yin' AND gender='m';
查詢條件能夠匹配索引的最左若干列。注意關鍵詞」最左前綴「。
-- 可使用部分索引"last_name" SELECT * FROM people WHERE last_name='zhang' AND gender='m'; -- 沒法使用索引 SELECT * FROM people WHERE first_name='zhang' AND gender='m';
查詢中的like條件,在有些場景下也可使用索引。如 last_name like 'zh%'可使用索引,而last_name like '%ing'則沒法使用索引。
-- 可使用索引,由於BTREE的節點比較key值時是從key值得最左側開始匹配 SELECT * FROM people WHERE last_name like 'zhang%' AND gender='m' ;
索引的列也支持範圍查詢。
SELECT * FROM people WHERE last_name > 'zhang' AND last_name <'wang'
ORDER BY語句在特定狀況下也支持用索引來排序來提升性能。
EXPLAIN SELECT * FROM people WHERE last_name = 'zhang' ORDER BY first_name ASC
1,查詢列不能參與表達式運算,不然沒法使用索引。
--表設計中沒有age列,以示參考 --假設age是索引中一部分,這樣的查詢將沒法使用到索引 SELECT * FROM people WHERE last_name='zhang' AND age+3>28; --這樣寫就可使用索引 SELECT * FROM people WHERE last_name='zhang' AND age>25;
2,若是不是從索引的最左列開始,則沒法使用索引。如,根據first_name、gender或者查找的查詢沒法使用索引。
-- 不是從last_name開始匹配,因此沒法使用索引 SELECT * FROM people WHERE first_name='zhang' AND gender='m'
3,不能跳過索引中的列。
-- 不能跳過first_name查詢,不然只有last_name列用到了索引 SELECT * FROM people WHERE last_name='zhang' AND gender='m'
4,若是查詢中某個列是範圍查詢(like,between,>,<等),則其右邊全部的列都沒法使用索引。
-- 因爲first_name用了like查詢,因此gender列沒法使用索引了 SELECT * FROM people WHERE last_name='zhang' AND first_name LIKE '%in' AND gender='m';
前面講到了各類可使用索引的查詢狀況,下面講如何創建高效的索引。
創建多列的索引,而不是每一列都創建單獨的。由於在mysql服務器在查詢分析後,最終只能根據查詢匹配到一個索引(或者沒有)並使用。因此,假設多列上分別都創建了單獨索引,即便組合查詢用到了多列,最終也只有一列用到了索引。
因此,假設你最多見的查詢是根據last_name、first_name和gender來查詢,應該創建包含三列的索引。
ALTER TABLE people ADD INDEX idx_name_gender(last_name, first_name , gender);
在多列B-TREE索引中,意味着索引是按照最左列開始,從左往右進行排序的。一個設計經驗法則,將」選擇性高「的列放在索引最左列。這樣有助於索引通過最少的比較找到目標元組。
索引列選擇性:不重複的索引值與表的所有記錄總數的比值,0<T<=1。惟一索引列的選擇性是1。索引的選擇性越高則查詢效率越高,能夠」更早地」過濾掉不匹配地記錄。
假設要創建 last_name, first_name , gender 三列的索引。
T(last_name)= select count(distinct last_name) / count(*) ;
T(first_name)= select count(distinct first_name) / count(*) ;
T(gender)= select count(distinct gender) / count(*) ;
很顯然,last_name和first_name應該放到索引的前面(以實際狀況爲主)
瞭解到了常見的索引策略和查詢技巧,可是怎麼在實際項目中應用並排查現存數據庫中sql的性能缺陷?下一篇將介紹mysql數據庫的explain關鍵字,總結和分析慢sql常見技巧。