數據庫會默認建立索引,可是並非給主鍵創建索引,而是給惟一鍵建裏索引的,由於主鍵的特性是惟一且非空java
CHAR,VARCHAR,TEXT
類型字段上使用全文索引
什麼是全文索引,就是在一堆文字中,經過其中的某個關鍵字等,就能找到該字段所屬的記錄行,好比有"LOL LPL 牧小農" 經過牧小農,可能就能夠找到該條記錄。這裏說的是可能,由於全文索引的使用涉及了不少細節,咱們只須要知道這個大概意思。通常開發中,不貴用到全文索引,由於其佔用很大的物理空間和下降了記錄修改性,故較爲少用。mysql
組合索引: 在表中的多個字段組合上建立的索引,只有在查詢條件中使用了這些字段的左邊字段時,索引纔會被使用,使用組合索引時遵循最左前綴集合。面試
例如這裏由id、name和age3個字段構成的索引,索引行中就按id/name/age的順序存放,索引能夠索引下面字段組合(id,name,age)、(id,name)或者(id)。若是要查詢的字段不構成索引最左面的前綴,那麼就不會是用索引,好比,age或者(name,age)組合就不會使用索引查詢。sql
1.4 面試技術名詞
最左匹配: 指在聯合索引中,若是你的 SQL 語句中用到了聯合索引中的最左邊的索引,那麼這條 SQL 語句就能夠利用這個聯合索引去進行匹配,若是遇到範圍查詢(>、<、between、like)就會中止匹配。數據庫
select from t where a=1 and b=1 and c =1; #這樣能夠利用到定義的索引(a,b,c),用上a,b,c
select from t where a=1 and b=1; #這樣能夠利用到定義的索引(a,b,c),用上a,b
select from t where b=1 and a=1; #這樣能夠利用到定義的索引(a,b,c),用上a,c(mysql有查詢優化器)
select from t where a=1; #這樣也能夠利用到定義的索引(a,b,c),用上a
select from t where b=1 and c=1; #這樣不能夠利用到定義的索引(a,b,c)
select from t where a=1 and c=1; #這樣能夠利用到定義的索引(a,b,c),但只用上a索引,b,c索引用不到緩存
1.5.1 哈希表
缺點︰性能優化
一、利用hash存儲的話須要將全部的數據文件添加到內存,比較耗費內存空間
二、若是全部的查詢都是等值查詢,那麼hash確實很快,可是在企業或者實際工做環境中範圍查找的數據更多,而不是等值查詢,所以hash就不太適合了服務器
1.5.2 二叉樹
缺點∶數據結構
不管是二叉樹仍是紅黑樹,都會由於樹的深度過深而形成io次數變多,影響數據讀取的效率ide
1.5.3 B+樹
B樹特色:
一、全部鍵值分佈在整顆樹中
二、搜索有可能在非葉子結點結束,在關鍵字全集內作一次查找,性能逼近二分查找
三、每一個節點最多擁有m個子樹
四、根節點至少有2個子樹
五、分支節點至少擁有m/2顆子樹(除根節點和葉子節點外都是分支節點)
六、全部葉子節點都在同一層、每一個節點最多能夠有m-1個key,而且以升序排列
實例圖說明∶
每一個節點佔用一個磁盤塊,一個節點上有兩個升序排序的關鍵字和三個指向子樹根節點的指針,指針存儲的是子節點所在磁盤塊的地址。兩個關鍵詞劃分紅的三個範圍域對應三個指針指向的子樹的數據的範圍域。以根節點爲例,關鍵字爲16和34,P1指針指向的子樹的數據範圍爲小於16,P2指針指向的子樹的數據範圍爲16~34 ,P3指針指向的子樹的數據範圍爲大於34。
查找關鍵字過程:
缺點:
全值匹配: 全值匹配指的是和索引中的全部列進行匹配
explain select * from staffs where name = 'July' and age = '23' and pos = 'dev';
匹配最左前綴: 只匹配前面的幾列
explain select * from staffs where name = 'July' and age = '23'; explain select * from staffs where name = 'July';
匹配列前綴: 能夠匹配某一列的值的開頭部分
explain select * from staffs where name like 'J%'; explain select * from staffs where name like '%y';
匹配範圍值: 能夠查找某一個範圍的數據
explain select * from staffs where name > 'Mary';
精確匹配某一列並範圍匹配另一列:能夠查詢第一列的所有和第二列的部分
explain select * from staffs where name = 'July' and age > 25;
只訪問索引的查詢: 查詢的時候只須要訪問索引,不須要訪問數據行,本質上就是覆蓋索引
explain select name,age,pos from staffs where name = 'July' and age = 25 and pos = 'dev';
當須要存儲大量的URL,而且根據URL進行搜索查找,若是使用B+樹,存儲的內容就會很大:select id from url where url=""
也能夠利用將url使用CRC32作哈希,可使用如下查詢方式:select id fom url where url="" and url_crc=CRC32("")
此查詢性能較高緣由是使用體積很小的索引來完成查找
當包含多個列做爲索引,須要注意的是正確的順序依賴於該索引的查詢,同時須要考慮如何更好的知足排序和分組的須要
案例: 創建組合索引 a,b,c ,不一樣SQL語句使用索引狀況
語句 | 索引是否發揮做用 |
---|---|
where a=3 | 是,只使用了a |
where a=3 and b=5 | 是,使用了a,b |
where a =3 and b = 5 and c= 4 | 是,使用了a,b,c |
where a = 3 or c = 4 | 否 |
where a = 3 and c= 4 | 是,僅使用了a |
where a = 3 and b > 10 and c = 7 | 是,使用了a,b |
where a = 3 and b like '%mxn%' and c=7 | 使用了a |
數據文件跟索引文件分開存放,將數據存儲於索引分開結構,索引結構的葉子節點指向了數據的對應行,myisam經過key_buffer把索引先緩存到內存中,當須要訪問數據時(經過索引訪問數據),在內存中直接搜索索引,而後經過索引找到磁盤相應數據,這也就是爲何索引不在key buffer命中時,速度慢的緣由
一、索引條目一般遠小於數據行大小,若是隻須要讀取索引,那麼mysql就會極大的較少數據訪問量
二、由於索引是按照列值順序存儲的,因此對於IO密集型的範圍查詢會比隨機從磁盤讀取每一行數據的IO要少的多
三、一些存儲引擎如MYISAM在內存中只緩存索引,數據則依賴於操做系統來緩存,所以要訪問數據須要一次系統調用,這可能會致使嚴重的性能問題
四、因爲INNODB的聚簇索引,覆蓋索引對INNODB表特別有用
一、當發起一個被索引覆蓋的查詢時,在explain的extra列能夠看到using index的信息,此時就使用了覆蓋索引
二、在大多數存儲引擎中,覆蓋索引只能覆蓋那些只訪問索引中部分列的查詢。不過,能夠進一步的進行優化,可使用innodb的二級索引來覆蓋查詢。
例如:actor使用innodb存儲引擎,並在last_name字段又二級索引,雖然該索引的列不包括主鍵actor_id,但也可以用於對actor_id作覆蓋查詢
有時候須要索引很長的字符串,這會讓索引變的大且慢,一般狀況下可使用某個列開始的部分字符串,這樣大大的節約索引空間,從而提升索引效率,但這會下降索引的選擇性,索引的選擇性是指不重複的索引值和數據表記錄總數的比值,範圍從1/#T到1之間。索引的選擇性越高則查詢效率越高,由於選擇性更高的索引可讓mysql在查找的時候過濾掉更多的行。
通常狀況下某個列前綴的選擇性也是足夠高的,足以知足查詢的性能,可是對應BLOB,TEXT,VARCHAR類型的列,必需要使用前綴索引,由於mysql不容許索引這些列的完整長度,使用該方法的訣竅在於要選擇足夠長的前綴以保證較高的選擇性,經過又不能太長。
--建立數據表 create table citydemo(city varchar(50) not null); insert into citydemo(city) select city from city; --重複執行5次下面的sql語句 insert into citydemo(city) select city from citydemo; --更新城市表的名稱 update citydemo set city=(select city from city order by rand() limit 1); --查找最多見的城市列表,發現每一個值都出現45-65次, select count(*) as cnt,city from citydemo group by city order by cnt desc limit 10; --查找最頻繁出現的城市前綴,先從3個前綴字母開始,發現比原來出現的次數更多,能夠分別截取多個字符查看城市出現的次數 select count(*) as cnt,left(city,3) as pref from citydemo group by pref order by cnt desc limit 10; select count(*) as cnt,left(city,7) as pref from citydemo group by pref order by cnt desc limit 10; --此時前綴的選擇性接近於完整列的選擇性 --還能夠經過另一種方式來計算完整列的選擇性,能夠看到當前綴長度到達7以後,再增長前綴長度,選擇性提高的幅度已經很小了 select count(distinct left(city,3))/count(*) as sel3, count(distinct left(city,4))/count(*) as sel4, count(distinct left(city,5))/count(*) as sel5, count(distinct left(city,6))/count(*) as sel6, count(distinct left(city,7))/count(*) as sel7, count(distinct left(city,8))/count(*) as sel8 from citydemo; --計算完成以後能夠建立前綴索引 alter table citydemo add key(city(7)); --注意:前綴索引是一種能使索引更小更快的有效方法,可是也包含缺點:mysql沒法使用前綴索引作order by 和 group by。
使用索引掃描來排序
mysql有兩種方式能夠生成有序的結果:經過排序操做或者按索引順序掃描,若是explain出來的type列的值爲index,則說明mysql使用了索引掃描來作排序
掃描索引自己是很快的,由於只須要從一條索引記錄移動到緊接着的下一條記錄。但若是索引不能覆蓋查詢所需的所有列,那麼就不得不每掃描一條索引記錄就得回表查詢一次對應的行,這基本都是隨機IO,所以按索引順序讀取數據的速度一般要比順序地全表掃描慢
mysql可使用同一個索引即知足排序,又用於查找行,若是可能的話,設計索引時應該儘量地同時知足這兩種任務。
只有當索引的列順序和order by子句的順序徹底一致,而且全部列的排序方式都同樣時,mysql纔可以使用索引來對結果進行排序,若是查詢須要關聯多張表,則只有當orderby子句引用的字段所有爲第一張表時,才能使用索引作排序。order by子句和查找型查詢的限制是同樣的,須要知足索引的最左前綴的要求,不然,mysql都須要執行順序操做,而沒法利用索引排序
強制類型轉換會全表掃描
create table user(id int,name varchar(10),phone varchar(11)); alter table user add index idx_1(phone); explain select * from user where phone=13800001234;(不會觸發索引) explain select * from user where phone='13800001234';(觸發索引)
更新十分頻繁,數據區分度不高的字段上不宜創建索引
更新會變動B+樹,更新頻繁的字段建議索引會大大下降數據庫性能.
相似於性別這類區分不大的屬性,創建索引是沒有意義的,不能有效的過濾數據,
通常區分度在80%以上的時候就能夠創建索引,區分度可使用 count(distinct(列名))/count(*) 來計算
索引越多越好(錯誤)過早優化,在不瞭解系統的狀況下進行優化(錯誤)