建立索引
#方法一:建立表時
CREATE TABLE 表名 (
字段名1 數據類型 [完整性約束條件…],
字段名2 數據類型 [完整性約束條件…],
[UNIQUE | FULLTEXT | SPATIAL ] INDEX | KEY
[索引名] (字段名[(長度)] [ASC |DESC])
);
#方法二:CREATE在已存在的表上建立索引
CREATE [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名
ON 表名 (字段名[(長度)] [ASC |DESC]) ;
#方法三:ALTER TABLE在已存在的表上建立索引
ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL ] INDEX
索引名 (字段名[(長度)] [ASC |DESC]) ;
#刪除索引:DROP INDEX 索引名 ON 表名字;
#方式一
create table t1(
id int,
name char,
age int,
sex enum('male','female'),
unique key uni_id(id),
index ix_name(name) #index沒有key
);
create table t1(
id int,
name char,
age int,
sex enum('male','female'),
unique key uni_id(id),
index(name) #index沒有key
);
#方式二
create index ix_age on t1(age);
#方式三
alter table t1 add index ix_sex(sex);
alter table t1 add index(sex);
#查看
mysql> show create table t1;
| t1 | CREATE TABLE `t1` (
`id` int(11) DEFAULT NULL,
`name` char(1) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`sex` enum('male','female') DEFAULT NULL,
UNIQUE KEY `uni_id` (`id`),
KEY `ix_name` (`name`),
KEY `ix_age` (`age`),
KEY `ix_sex` (`sex`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
例子
例子
測試索引
數據準備
#1. 準備表
create table s1(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);
#2. 建立存儲過程,實現批量插入記錄
delimiter $$ #聲明存儲過程的結束符號爲$$
create procedure auto_insert1()
BEGIN
declare i int default 1;
while(i<3000000)do
insert into s1 values(i,'eva','female',concat('eva',i,'@oldboy'));
set i=i+1;
end while;
END$$ #$$結束
delimiter ; #從新聲明分號爲結束符號
#3. 查看存儲過程
show create procedure auto_insert1\G
#4. 調用存儲過程
call auto_insert1();
View Code
在沒有索引的前提下測試查詢速度
#無索引:mysql根本就不知道究竟是否存在id等於333333333的記錄,只能把數據表從頭至尾掃描一遍,此時有多少個磁盤塊就須要進行多少IO操做,因此查詢速度很慢
mysql> select * from s1 where id=333333333;
Empty set (0.33 sec)
在表中已經存在大量數據的前提下,爲某個字段段創建索引,創建速度會很慢
![](http://static.javashuo.com/static/loading.gif)
在索引創建完畢後,以該字段爲查詢條件時,查詢速度提高明顯
![](http://static.javashuo.com/static/loading.gif)
PS:html
1. mysql先去索引表裏根據b+樹的搜索原理很快搜索到id等於333333333的記錄不存在,IO大大下降,於是速度明顯提高mysql
2. 咱們能夠去mysql的data目錄下找到該表,能夠看到佔用的硬盤空間多了sql
3. 須要注意,以下圖ide
總結:
#1. 必定是爲搜索條件的字段建立索引,好比select * from s1 where id = 333;就須要爲id加上索引
#2. 在表中已經有大量數據的狀況下,建索引會很慢,且佔用硬盤空間,建完後查詢速度加快
好比create index idx on s1(id);會掃描表中全部的數據,而後以id爲數據項,建立索引結構,存放於硬盤的表中。
建完之後,再查詢就會很快了。
#3. 須要注意的是:innodb表的索引會存放於s1.ibd文件中,而myisam表的索引則會有單獨的索引文件table1.MYI
MySAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在innodb中,表數據文件自己就是按照B+Tree(BTree即Balance True)組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,所以innodb表數據文件自己就是主索引。
由於inndob的數據文件要按照主鍵彙集,因此innodb要求表必需要有主鍵(Myisam能夠沒有),若是沒有顯式定義,則mysql系統會自動選擇一個能夠惟一標識數據記錄的列做爲主鍵,若是不存在這種列,則mysql會自動爲innodb表生成一個隱含字段做爲主鍵,這字段的長度爲6個字節,類型爲長整型.
若是才能正確命中索引
索引未命中
並非說咱們建立了索引就必定會加快查詢速度,若想利用索引達到預想的提升查詢速度的效果,咱們在添加索引時,必須遵循如下問題
函數
範圍問題,或者說條件不明確
條件中出現這些符號或關鍵字:>、>=、<、<=、!= 、between...and...、like、post
大於號、小於號測試
![](http://static.javashuo.com/static/loading.gif)
不等於!=
![](http://static.javashuo.com/static/loading.gif)
between ...and...
![](http://static.javashuo.com/static/loading.gif)
like
![](http://static.javashuo.com/static/loading.gif)
區分度問題
儘可能選擇區分度高的列做爲索引,區分度的公式是count(distinct col)/count(*),表示字段不重複的比例,比例越大咱們掃描的記錄數越少,惟一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0,那可能有人會問,這個比例有什麼經驗值嗎?使用場景不一樣,這個值也很難肯定,通常須要join的字段咱們都要求是0.1以上,即平均1條掃描10條記錄大數據
mysql> desc s1;
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id | int(11) | YES | MUL | NULL | |
| name | varchar(20) | YES | | NULL | |
| gender | char(5) | YES | | NULL | |
| email | varchar(50) | YES | MUL | NULL | |
+--------+-------------+------+-----+---------+-------+
rows in set (0.00 sec)
mysql> drop index a on s1;
Query OK, 0 rows affected (0.20 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> drop index d on s1;
Query OK, 0 rows affected (0.18 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc s1;
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| gender | char(5) | YES | | NULL | |
| email | varchar(50) | YES | | NULL | |
+--------+-------------+------+-----+---------+-------+
rows in set (0.00 sec)
先把表中的索引都刪除,讓咱們專心研究區分度的問題
先把表中的索引都刪除,讓咱們專心研究區分度的問題
![](http://static.javashuo.com/static/loading.gif)
咱們編寫存儲過程爲表s1批量添加記錄,name字段的值均爲egon,也就是說name這個字段的區分度很低(gender字段也是同樣的,咱們稍後再搭理它)
回憶b+樹的結構,查詢的速度與樹的高度成反比,要想將樹的高低控制的很低,須要保證:在某一層內數據項均是按照從左到右,從小到大的順序依次排開,即左1<左2<左3<...
而對於區分度低的字段,沒法找到大小關係,由於值都是相等的,毫無疑問,還想要用b+樹存放這些等值的數據,只能增長樹的高度,字段的區分度越低,則樹的高度越高。極端的狀況,索引字段的值都同樣,那麼b+樹幾乎成了一根棍。本例中就是這種極端的狀況,name字段全部的值均爲'egon'
#如今咱們得出一個結論:爲區分度低的字段創建索引,索引樹的高度會很高,然而這具體會帶來什麼影響呢???
#1:若是條件是name='xxxx',那麼確定是能夠第一時間判斷出'xxxx'是不在索引樹中的(由於樹中全部的值均爲'egon’),因此查詢速度很快
#2:若是條件正好是name='egon',查詢時,咱們永遠無
分析緣由
分析緣由
索引列不能在條件中參與計算
索引列不能在條件中參與計算,保持列「乾淨」,好比from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,緣由很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,須要把全部元素都應用函數才能比較,顯然成本太大。因此語句應該寫成create_time = unix_timestamp(’2014-05-29’)url
![](http://static.javashuo.com/static/loading.gif)
and/or
#一、and與or的邏輯
條件1 and 條件2:全部條件都成立纔算成立,但凡要有一個條件不成立則最終結果不成立
條件1 or 條件2:只要有一個條件成立則最終結果就成立
#二、and的工做原理
條件:
a = 10 and b = 'xxx' and c > 3 and d =4
索引:
製做聯合索引(d,a,b,c)
工做原理:
對於連續多個and:mysql會按照聯合索引,從左到右的順序找一個區分度高的索引字段(這樣即可以快速鎖定很小的範圍),加速查詢,即按照d—>a->b->c的順序
#三、or的工做原理
條件:
a = 10 or b = 'xxx' or c > 3 or d =4
索引:
製做聯合索引(d,a,b,c)
工做原理:
對於連續多個or:mysql會按照條件的順序,從左到右依次判斷,即a->b->c->d
![](http://static.javashuo.com/static/loading.gif)
在左邊條件成立可是索引字段的區分度低的狀況下(name與gender均屬於這種狀況),會依次往右找到一個區分度高的索引字段,加速查詢spa
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
通過分析,在條件爲name='egon' and gender='male' and id>333 and email='xxx'的狀況下,咱們徹底不必爲前三個條件的字段加索引,由於只能用上email字段的索引,前三個字段的索引反而會下降咱們的查詢效率
![](http://static.javashuo.com/static/loading.gif)
最左前綴匹配原則
很是重要的原則,對於組合索引mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就中止匹配(指的是範圍大了,有索引速度也慢),好比a = 1 and b = 2 and c > 3 and d = 4 若是創建(a,b,c,d)順序的索引,d是用不到索引的,若是創建(a,b,d,c)的索引則均可以用到,a,b,d的順序能夠任意調整。
![](http://static.javashuo.com/static/loading.gif)
其餘狀況
使用函數
select * from tb1 where reverse(email) = 'egon';
- 類型不一致
若是列是字符串類型,傳入條件是必須用引號引發來,否則...
select * from tb1 where email = 999;
#排序條件爲索引,則select字段必須也是索引字段,不然沒法命中
- order by
select name from s1 order by email desc;
當根據索引排序時候,select查詢的字段若是不是索引,則速度仍然很慢
select email from s1 order by email desc;
特別的:若是對主鍵排序,則仍是速度很快:
select * from tb1 order by nid desc;
- 組合索引最左前綴
若是組合索引爲:(name,email)
name and email -- 命中索引
name -- 命中索引
email -- 未命中索引
- count(1)或count(列)代替count(*)在mysql中沒有差異了
- create index xxxx on tb(title(19)) #text類型,必須制定長度
其餘注意事項
- 避免使用select *
- 使用count(*)
- 建立表時儘可能使用 char 代替 varchar
- 表的字段順序固定長度的字段優先
- 組合索引代替多個單列索引(因爲mysql中每次只能使用一個索引,因此常用多個條件查詢時更適合使用組合索引)
- 儘可能使用短索引
- 使用鏈接(JOIN)來代替子查詢(Sub-Queries)
- 連表時注意條件類型需一致
- 索引散列值(重複少)不適合建索引,例:性別不適合
聯合索引
聯合索引
聯合索引是指對錶上的多個列合起來作一個索引。聯合索引的建立方法與單個索引的建立方法同樣,不一樣之處僅在於有多個索引列,如:
mysql> create table t(
-> a int,
-> b int,
-> primary key(a),
-> key idx_a_b(a,b)
-> );
Query OK, 0 rows affected (0.11 sec)
覆蓋索引
InnoDB存儲引擎支持覆蓋索引(covering index,或稱索引覆蓋),即從輔助索引中就能夠獲得查詢記錄,而不須要查詢彙集索引中的記錄。
覆蓋索引 查一個數據不須要回表
select name from 表 where age = 20 不是覆蓋索引
select age from 表 where age =20 是覆蓋索引
select count(age) from 表 where age =20 是覆蓋索引
合併索引
當咱們爲單獨的一列建立索引的時候
若是條件是這一列,且使用正確就能夠命中索引
當咱們爲兩列分別建立單獨的索引的時候
若是這兩列都是條件,那麼可能只能命中期中一個條件
若是這兩列都是條件,那麼可能會命中兩個索引 - 合併索引
咱們爲多列直接建立聯合因此
條件命中聯合索引
索引總結:
1.條件必定是創建了索引的字段,若是條件使用的字段根本就沒有建立索引,那麼索引不生效
2.若是條件是一個範圍,隨着範圍的值逐漸增大,那麼索引能發揮的做用也越小
3.若是使用like進行模糊查詢,那麼使用a%的形式能命中索引,%a形式不能命中索引
4.儘可能選擇區分度高的字段做爲索引列
5.索引列不能在條件中參與計算,也不能使用函數
6.在多個條件以and相連的時候,會優勢選擇區分度高的索引列來進行查詢
在多個條件以or相連的時候,就是從左到右依次判斷
7.製做聯合索引
1.最左前綴原則 a,b,c,d 條件是a的能命中索引,條件是a,b能命中索引,a,b,c能命中,a,c.... 只要沒有a就不能命中索引
若是在聯合查詢中,老是涉及到同一個字段,那麼就在創建聯合索引的時候將這個字段放在最左側
2.聯合索引 若是按照定義順序,從左到右遇到的第一個在條件中以範圍爲條件的字段,索引失效
儘可能將帶着範圍查詢的字段,定義在聯合索引的最後面
drop index
若是咱們查詢的條件老是多個列合在一塊兒查,那麼就創建聯合索引
create index ind_mix on s1(id,email)
select * from s1 where id = 1000000 命中索引
select * from s1 where email = 'eva1000000@oldboy' 未命中索引
但凡是建立了聯合索引,那麼在查詢的時候,再建立順序中從左到右的第一列必須出如今條件中
select count(*) from s1 where id = 1000000 and email = 'eva10%'; 命中索引
select count(*) from s1 where id = 1000000 and email like 'eva10%'; 能夠命中索引
範圍 :
select * from s1 where id >3000 and email = 'eva300000@oldboy'; 不能命中索引
8.條件中涉及的字段的值必須和定義表中字段的數據類型一致,不然不能命中索引
執行計劃
執行計劃
看看mysql準備怎麼執行這條語句 能夠看到是否命中索引,計劃能命中哪些,實際命中了哪些,執行的順序,是否發生了索引合併,覆蓋索引
explain select * from s1;