索引是存儲引擎用於快速查找記錄的一種數據結構,經過合理的使用數據庫索引能夠大大提升系統的訪問性能,接下來主要介紹在MySql數據庫中索引類型,以及如何建立出更加合理且高效的索引技巧。
注:這裏主要針對的是InnoDB存儲引擎的B+Tree索引數據結構
二、索引的優勢
一、大大減輕了服務器須要掃描的數據量,從而提升了數據的檢索速度
二、幫助服務器避免排序和臨時表
三、能夠將隨機I/O變爲順序I/O
三、索引的建立
3.一、主鍵索引java
ALTER TABLE 'table_name' ADD PRIMARY KEY 'index_name' ('column');
3.二、惟一索引數據庫
ALTER TABLE 'table_name' ADD UNIQUE 'index_name' ('column');
3.三、普通索引服務器
ALTER TABLE 'table_name' ADD INDEX 'index_name' ('column');
3.四、全文索引數據結構
ALTER TABLE 'table_name' ADD FULLTEXT 'index_name' ('column');
3.五、組合索引函數
ALTER TABLE 'table_name' ADD INDEX 'index_name' ('column1', 'column2', ...);
四、B+Tree的索引規則
建立一個測試的用戶表性能
DROP TABLE IF EXISTS user_test; CREATE TABLE user_test( id int AUTO_INCREMENT PRIMARY KEY, user_name varchar(30) NOT NULL, sex bit(1) NOT NULL DEFAULT b'1', city varchar(50) NOT NULL, age int NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
建立一個組合索引:測試
ALTER TABLE user_test ADD INDEX idx_user(user_name , city , age);
4.一、索引有效的查詢
4.1.一、全值匹配
全值匹配指的是和索引中的全部列進行匹配,如:以上面建立的索引爲例,在where條件後可同時查詢(user_name,city,age)爲條件的數據。優化
SELECT * FROM user_test WHERE user_name = 'feinik' AND age = 26 AND city = '天津';
4.1.二、匹配最左前綴
匹配最左前綴是指優先匹配最左索引列,如:上面建立的索引可用於查詢條件爲:ui
(user_name )、(user_name, city)、(user_name , city , age)
注:知足最左前綴查詢條件的順序與索引列的順序無關,如:spa
(city, user_name)、(age, city, user_name)
4.1.三、匹配列前綴
指匹配列值的開頭部分,如:查詢用戶名以feinik開頭的全部用戶
SELECT * FROM user_test WHERE user_name LIKE 'feinik%';
4.1.四、匹配範圍值
如:查詢用戶名以feinik開頭的全部用戶,這裏使用了索引的第一列
SELECT * FROM user_test WHERE user_name LIKE 'feinik%';
4.二、索引的限制
一、where查詢條件中不包含索引列中的最左索引列,則沒法使用到索引查詢,如:
SELECT * FROM user_test WHERE city = '天津';
或
SELECT * FROM user_test WHERE age= 26;
或
SELECT * FROM user_test WHERE city = '天津' AND age = '26';
二、即便where的查詢條件是最左索引列,也沒法使用索引查詢用戶名以feinik結尾的用戶
SELECT * FROM user_test WHERE user_name like '%feinik';
三、若是where查詢條件中有某個列的範圍查詢,則其右邊的全部列都沒法使用索引優化查詢,如:
SELECT * FROM user_test WHERE user_name = 'feinik' AND city LIKE '廣州%' AND age = 26;
五、高效的索引策略
5.一、索引列不能是表達式的一部分,也不能做爲函數的參數,不然沒法使用索引查詢。
SELECT * FROM user_test WHERE user_name = concat(user_name, 'fei');
5.二、前綴索引
有時候須要索引很長的字符列,這會增長索引的存儲空間以及下降索引的效率,一種策略是可使用哈希索引,還有一種就是可使用前綴索引,前綴索引是選擇字符列的前n個字符做爲索引,這樣能夠大大節約索引空間,從而提升索引效率。
5.2.一、前綴索引的選擇性
前綴索引要選擇足夠長的前綴以保證高的選擇性,同時又不能太長,咱們能夠經過如下方式來計算出合適的前綴索引的選擇長度值:
(1)
SELECT COUNT(DISTINCT index_column)/COUNT(*) FROM table_name; -- index_column
表明要添加前綴索引的列
(2)
SELECT COUNT(DISTINCT LEFT(index_column,1))/COUNT(*), COUNT(DISTINCT LEFT(index_column,2))/COUNT(*), COUNT(DISTINCT LEFT(index_column,3))/COUNT(*) ... FROM table_name;
注:經過以上語句逐步找到最接近於(1)中的前綴索引的選擇性比值,那麼就可使用對應的字符截取長度來作前綴索引了
5.2.二、前綴索引的建立
5.2.三、使用前綴索引的注意點
前綴索引是一種能使索引更小,更快的有效辦法,可是MySql沒法使用前綴索引作ORDER BY 和 GROUP BY以及使用前綴索引作覆蓋掃描。
5.三、選擇合適的索引列順序
在組合索引的建立中索引列的順序很是重要,正確的索引順序依賴於使用該索引的查詢方式,對於組合索引的索引順序能夠經過經驗法則來幫助咱們完成:將選擇性最高的列放到索引最前列,該法則與前綴索引的選擇性方法一致,但並非說全部的組合索引的順序都使用該法則就能肯定,還須要根據具體的查詢場景來肯定具體的索引順序。
5.4 彙集索引與非彙集索引
一、彙集索引
彙集索引決定數據在物理磁盤上的物理排序,一個表只能有一個彙集索引,若是定義了主鍵,那麼InnoDB會經過主鍵來彙集數據,若是沒有定義主鍵,InnoDB會選擇一個惟一的非空索引代替,若是沒有惟一的非空索引,InnoDB會隱式定義一個主鍵來做爲彙集索引。
彙集索引能夠很大程度的提升訪問速度,由於彙集索引將索引和行數據保存在了同一個B-Tree中,因此找到了索引也就相應的找到了對應的行數據,但在使用匯集索引的時候需注意避免隨機的彙集索引(通常指主鍵值不連續,且分佈範圍不均勻),如使用UUID來做爲彙集索引性能會不好,由於UUID值的不連續會致使增長不少的索引碎片和隨機I/O,最終致使查詢的性能急劇降低。
二、非彙集索引
與彙集索引不一樣的是非彙集索引並不決定數據在磁盤上的物理排序,且在B-Tree中包含索引但不包含行數據,行數據只是經過保存在B-Tree中的索引對應的指針來指向行數據,如:上面在(user_name,city, age)上創建的索引就是非彙集索引。
5.五、覆蓋索引
若是一個索引(如:組合索引)中包含全部要查詢的字段的值,那麼就稱之爲覆蓋索引,如:
由於要查詢的字段(user_name, city, age)都包含在組合索引的索引列中,因此就使用了覆蓋索引查詢,查看是否使用了覆蓋索引能夠經過執行計劃中的Extra中的值爲Using index則證實使用了覆蓋索引,覆蓋索引能夠極大的提升訪問性能。
5.六、如何使用索引來排序
在排序操做中若是能使用到索引來排序,那麼能夠極大的提升排序的速度,要使用索引來排序須要知足如下兩點便可。
一、ORDER BY子句後的列順序要與組合索引的列順序一致,且全部排序列的排序方向(正序/倒序)需一致
二、所查詢的字段值須要包含在索引列中,及知足覆蓋索引
經過例子來具體分析
在user_test表上建立一個組合索引
ALTER TABLE user_test ADD INDEX index_user(user_name , city , age);
可使用到索引排序的案例
一、SELECT user_name, city, age FROM user_test ORDER BY user_name; 二、SELECT user_name, city, age FROM user_test ORDER BY user_name, city; 三、SELECT user_name, city, age FROM user_test ORDER BY user_name DESC, city DESC; 四、SELECT user_name, city, age FROM user_test WHERE user_name = 'feinik' ORDER BY city;
注:第4點比較特殊一點,若是where查詢條件爲索引列的第一列,且爲常量條件,那麼也可使用到索引
沒法使用索引排序的案例
一、sex不在索引列中
SELECT user_name, city, age FROM user_test ORDER BY user_name, sex;
二、排序列的方向不一致
SELECT user_name, city, age FROM user_test ORDER BY user_name ASC, city DESC;
三、所要查詢的字段列sex沒有包含在索引列中
SELECT user_name, city, age, sex FROM user_test ORDER BY user_name;
四、where查詢條件後的user_name爲範圍查詢,因此沒法使用到索引的其餘列
SELECT user_name, city, age FROM user_test WHERE user_name LIKE 'feinik%' ORDER BY city;
五、多表鏈接查詢時,只有當ORDER BY後的排序字段都是第一個表中的索引列(須要知足以上索引排序的兩個規則)時,方可以使用索引排序。如:再建立一個用戶的擴展表user_test_ext,並創建uid的索引。
DROP TABLE IF EXISTS user_test_ext; CREATE TABLE user_test_ext( id int AUTO_INCREMENT PRIMARY KEY, uid int NOT NULL, u_password VARCHAR(64) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE user_test_ext ADD INDEX index_user_ext(uid);
走索引排序
SELECT user_name, city, age FROM user_test u LEFT JOIN user_test_ext ue ON u.id = ue.uid ORDER BY u.user_name;
不走索引排序
SELECT user_name, city, age FROM user_test u LEFT JOIN user_test_ext ue ON u.id = ue.uid ORDER BY ue.uid;