MySQL索引選擇及規則整理
索引選擇性
索引選擇性就是結果個數與總個數的比值。
用sql語句表示為:
SELECT COUNT(*) FROM table_name WHERE column_name/SELECT COUNT(*) FROM table_name
一般來說(例如書 “SQL Tuning“),如果選擇性超過 20% 那麽全表掃描比使用索引性能更優。
但MySQL是沒有計算索引的選擇性的,只是預測邏輯IO操作的數量,因此對於MySQL索引要慎重選擇。
舉個栗子,tinyint類型的列,用以保存性別,就算用上“保密”和“變性”2項,選擇性也最小也才25%,因此也就沒有設置索引的必要了。
組合索引
假設test表中有a,b,c三個列。
ALTER TABLE test ADD INDEX abc(a,b,c);
相當於分別建立了
a,b,c
a,b
a
這樣的3組索引,也是“最左前綴”這個規則的結果。
舉個使用該組合索引的栗子:
SELECT * FROM test WHERE a="1" AND b="2" SELECT * FROM test WHERE a="1"
以下則用不到索引:
SELECT * FROM test WHERE b="1" AND c="2" SELECT * FROM test WHERE c="1"
因此組合索引有一定優勢,但在使用上需謹慎。
前綴索引
MySQL 前綴索引能有效減小索引文件的大小,提高索引的速度。但是前綴索引也有它的壞處:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前綴索引,也不能把它們用作覆蓋索引(Covering Index)。
語法如下:
ALTER TABLE table_name ADD KEY column_name(prefix_length);
建立前綴索引的關鍵在於"prefix_length"這個參數,並且前綴索引的選擇性上也有一點特殊。
前綴索引的選擇性公式為:SELECT COUNT(DISTINCT column_name)/COUNT(*) FROM table_name
現在有個user表,列 family_name varchar(50) 保存的是英文姓氏(我也想用中文姓名來舉例,但是不大適合,看下去就明白了。。。)
要取得設置前綴索引最理想的"prefix_length",我們首先要取得整列的選擇性,如下:
SELECT COUNT(DISTINCT family_name)/COUNT(*) FROM user;
假設這裏得到值是0.188。
然後我們繼續去看看該列前1個字符的選擇性又是多少
SELECT COUNT(DISTINCT LEFT(family_name,1))/COUNT(*) FROM user;
假設這裏得到的結果是0.532,和整列的選擇性出入太大,不可取,繼續:
SELECT COUNT(DISTINCT LEFT(family_name,2))/COUNT(*) FROM user;
SELECT COUNT(DISTINCT LEFT(family_name,3))/COUNT(*) FROM user;
...
假設直接到“prefix_length”為5時,得到的值為0.189,非常接近!
而取6時得到的值為0.18891,這個選擇性和5並沒有太大的偏差。
再結合減少索引文件大小的這個思路
“prefix_length”值設置為5才是此處設置前綴索引的最優方案!
選擇性講完,還得再講清楚這個前綴索引該怎麽用!
書接上面的栗子~
正確的用法如下:
SELECT * FROM user WHERE family_name LIKE "lee%";
SELECT * FROM user WHERE family_name LIKE "david%";
以下則用不上該索引:
SELECT * FROM user WHERE family_name LIKE "_ee%";
SELECT * FROM user WHERE family_name LIKE "%en%";
SELECT * FROM user WHERE family_name LIKE "%ar%";
註意:SQL的模式缺省是忽略大小寫的!
另外,“_”代表一個字符,“%”代表任意多個字符!
MySQL索引選擇及規則整理