mysql數據庫索引優化與實踐(一)

前言

mysql數據庫是如今應用最普遍的數據庫系統。與數據庫打交道是每一個Java程序員平常工做之一,索引優化是必備的技能之一。mysql

爲何要了解索引

真實案例

案例一:大學有段時間學習爬蟲,爬取了知乎300w用戶答題數據,存儲到mysql數據中。那時不瞭解索引,一條簡單的「根據用戶名搜索所有回答的sql「須要執行半分鐘左右,徹底知足不了正常的使用。程序員

案例二:最近線上應用的數據庫頻頻出現多條慢sql風險提示,而工做以來,對數據庫優化方面所知甚少。例如一個用戶數據頁面須要執行不少次數據庫查詢,性能很慢,經過增長超時時間勉強能夠訪問,可是性能上須要優化。算法

索引的優勢

合適的索引,能夠大大減少mysql服務器掃描的數據量,避免內存排序和臨時表,提升應用程序的查詢性能。sql

索引的類型

mysql數據中有多種索引類型,primary key,unique,normal,但底層存儲的數據結構都是BTREE;有些存儲引擎還提供hash索引,全文索引。數據庫

BTREE是最多見的優化要面對的索引結構,都是基於BTREE的討論。服務器

B-TREE

查詢數據最簡單暴力的方式是遍歷全部記錄;若是數據不重複,就能夠經過組織成一顆排序二叉樹,經過二分查找算法來查詢,大大提升查詢性能。而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';

高效索引策略

前面講到了各類可使用索引的查詢狀況,下面講如何創建高效的索引。

1,創建多列索引

 創建多列的索引,而不是每一列都創建單獨的。由於在mysql服務器在查詢分析後,最終只能根據查詢匹配到一個索引(或者沒有)並使用。因此,假設多列上分別都創建了單獨索引,即便組合查詢用到了多列,最終也只有一列用到了索引。

   因此,假設你最多見的查詢是根據last_name、first_name和gender來查詢,應該創建包含三列的索引。

ALTER TABLE people ADD INDEX idx_name_gender(last_name, first_name , gender);

2,索引列的順序

在多列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常見技巧。

相關文章
相關標籤/搜索