建立高效能mysql索引
1 索引基礎
1.1 索引型別
B-tree索引(具體性質參見B-tree資料結構):所有值按照順序儲存,每一個葉子頁到根的距離相同,索引的順序很重要。有索引key(last_name,first_name,dob)。可查詢如下型別:
-
全值匹配:如Cuba Allen,1960-01-01
-
匹配最左字首:如Allen,即只使用索引的第一列。
-
匹配列字首:查詢所有以J開頭的姓的人,使用索引第一列
-
匹配範圍值:姓在Allen和BarryMore之間的人,使用索引第一列。
-
精確匹配某一列並範圍匹配另外一列:第一列全匹配,第二列範圍匹配。
-
只訪問索引的查詢
B-tree索引的限制:
-
如果不是按照索引的最左列開始查詢,則無法使用索引。如索引無法查詢名字為bill的人,也無法查詢特定生日的人,也無法查詢姓氏以某個字母結尾的人。
-
不能跳過索引中的列。索引無法用於查詢姓為smith並且在某個特定日期出生的人,如不指定名(first_name),則mysql只能使用索引第一列。
-
如果查詢中有某個列的範圍查詢,則其右邊所有列都無法使用索引優化查詢。如有查詢WHERE last_name=’Smith’ AND frist_name LIKE ‘J%’ AND dob=’1976-01-01’。這個查詢只能使用索引的前兩列。
雜湊索引
基於雜湊表實現,只有精確匹配索引所有列的查詢才有效。在mysql中,只有momery引擎顯示支援雜湊索引。
空間資料索引(R-Tree)
MyISAM表支援空間索引,可以用作地理資料索引,這類索引無需字首查詢。
全文索引
是一種特殊的索引,它查詢的時文字中的關鍵詞,而不是直接比較索引中的值。
2 索引優點
-
大大減少了伺服器需要掃描的資料量
-
幫助伺服器避免排序和臨時表
-
可以將隨機io變為順序io。
非常小的表,大部分情況下簡單的全表掃描更高效。中到大型表,索引非常有效。特大型表需要使用分割槽技術。
3 高效能的索引策略
3.1 獨立的列
始終將索引列單獨放在比較符號的一側。
3.2 字首索引和索引選擇性
當索引很長的字元列,會讓索引變的大且慢。一個策略是模擬雜湊索引,還可以索引開始的部分字元,這樣可以大大節約索引空間,提高索引效率。一般當前綴長度達到7的時候,再增加字首長度,選擇性提升的幅度已經很小了。
建立字首索引:alter table sakila.city_demo add key(city(7))
字首索引的缺點:無法使用字首索引做order by和group by,也無法使用字首索引做覆蓋掃描。
3.3 多列索引
如索引:key(last_name,first_name,dob)
3.4 選擇合適的索引列順序
正確的順序依賴於使用該索引的查詢,並且同時需要考慮如何更好地滿足排序和分組的需要。在一個多列btree索引中,索引列的順序意味著索引首先按照最左列進行排序,其次是第二列,等等。多列索引的列順序至關重要。經驗做法:將選擇性最高的列放在索引最前列。或者使用pt-query-digest工具進行分析。
3.5 聚簇索引
聚簇索引並不是一種單獨的索引型別,而是一種資料儲存方式,一個表只能有一個聚簇索引。InnoDB將通過逐漸聚集資料,如果沒有定義主鍵,InnoDB會選擇一個唯一的非空索引代替。如果沒有這樣的索引,InnoDB會隱式定義一個主鍵來作為聚簇索引。使用InnoDB時應該儘可能地按主鍵順序插入資料,並且儘可能地使用單調增加的聚簇鍵的值來插入新行。
在InnoDB中,聚簇索引就是表,InnoDB的二級索引和聚簇索引很不同。InnoDB二級索引的頁子節點中儲存的不是“行指標”,而是主鍵值,並以此作為指向行的“指標”。
主索引:建立表啟用後由系統自動建立的,不能修改
二級索引:自己建立的索引
3.6 覆蓋索引
如果一個索引包含所有需要查詢的欄位的值,我麼就稱之為覆蓋索引。覆蓋索引能夠極大提高效能,只需要掃描索引而無須回表。覆蓋索引必須要儲存索引列的值,所以mysql只能使用btree索引做覆蓋索引。
錯誤示例:select * from products where actor=’sean carrey’ and title like ‘%apollo%’\G
這裡索引無法覆蓋該查詢:1查詢從表中選擇了所有得咧,而沒有任何索引覆蓋了所有得列。2 mysql不能在索引中執行like操作,只能在索引中做最左匹配字首匹配。
優化:select * from products join (select prod_id from products where actor=’sean carrey’ and title like ‘%apollo%’) as t1 on (t1.prod_id=products.prod_id)\G
3.7 使用索引掃描來做排序
如果explain出來的type列值為’index’,則說明mysql使用了索引掃描來做排序。
只有當索引的列順序和order by子句的順序完全一致,並且所有列的排序方向都一樣,mysql才能夠使用索引來對結果做排序。如果查詢需要關聯多張表,則只有當order by子句引用的欄位全部為第一個表時,才能使用索引做排序。order by子句需要滿足索引最左字首的要求,如果不滿足,前導列為常量的時候,可以利用索引排序。
Key rental_date(rental_date,inventory_id,customer_id)
select rental_id,staff_id from sakila.rental where rental_date=’2005-05-05’ order by inventory_id,customer_id\G
order by子句不滿足索引最左字首要求,也可以用於查詢排序,因為索引第一列被指定為一個常數。
下列示例不能使用索引做排序查詢
查詢使用了兩個不同的排序方向
..where rental_data=’2005-05-05’ order by inventory_id desc,customer_id asc;
order by使用了一個不在索引中的列
..where rental_date=’2005-05-05’ order by inventory_id,staff_id;
where和order by中的列無法組合成索引的最左字首。
..where rental_date=’2005-05-05’ order by customer_id;
查詢在索引的第一列上時範圍條件,mysql無法使用索引的其餘列
..where rental_date>’2005-05-05’ order by inventory_id,customer_id;
某一列上有多個等於條件,對於排序來說,這也是一種範圍查詢
..where rental_date=’2005-05-05’ and inventory_id in(1,2) order by customer_id;
3.8 壓縮(字首)索引
可以在create table語句中指定pack_keys引數來控制索引壓縮方式。
3.9 冗餘索引和重複索引
刪除,工具common_shema或pt-duplicate-key-checker
3.10 未使用的索引
刪除,工具pt-index-usage
3.11 索引和鎖
InnoDB在訪問行的時候會對其加鎖,索引能夠減少InnoDB訪問的行數,從而減少鎖的數量。如果索引無法過濾掉無效的行,那麼在InnoDB檢索到資料並返回給服務層後,mysql伺服器才能應用where子句。