數(shù)據(jù)庫會默認創(chuàng)建索引,但是并不是給主鍵建立索引,而是給唯一鍵建立索引的,因為主鍵的特性是唯一且非空
例如這里由id、name和age3個字段構(gòu)成的索引,索引行中就按id/name/age的順序存放,索引可以索引下面字段組合(id,name,age)、(id,name)或者(id)。如果要查詢的字段不構(gòu)成索引最左面的前綴,那么就不會使用索引,比如,age或者(name,age)組合就不會使用索引查詢。
回表: 數(shù)據(jù)庫根據(jù)索引(非主鍵)找到了指定的記錄所在行后,還需要根據(jù)主鍵再次到數(shù)據(jù)塊里獲取數(shù)據(jù),這種稱之為回表
覆蓋索引: 看我寫的一篇文章:面試三輪我倒在了一道sql題上——sql性能優(yōu)化
最左匹配: 指在聯(lián)合索引中,如果你的 SQL 語句中用到了聯(lián)合索引中的最左邊的索引,那么這條 SQL 語句就可以利用這個聯(lián)合索引去進行匹配,如果遇到范圍查詢(>、、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有查詢優(yōu)化器) 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索引用不到
索引下推: 稱為 Index Condition Pushdown (ICP),這是MySQL提供的用某一個索引對一個特定的表從表中獲取元組”,注意我們這里特意強調(diào)了“一個”,這是因為這樣的索引優(yōu)化不是用于多表連接而是用于單表掃描,確切地說,是單表利用索引進行掃描以獲取數(shù)據(jù)的一種方式。
缺點︰
1、利用hash存儲的話需要將所有的數(shù)據(jù)文件添加到內(nèi)存,比較耗費內(nèi)存空間
2、如果所有的查詢都是等值查詢,那么hash確實很快,但是在企業(yè)或者實際工作環(huán)境中范圍查找的數(shù)據(jù)更多,而不是等值查詢,因此hash就不太適合了
缺點∶
無論是二叉樹還是紅黑樹,都會因為樹的深度過深而造成io次數(shù)變多,影響數(shù)據(jù)讀取的效率
B樹特點:
1、所有鍵值分布在整顆樹中
2、搜索有可能在非葉子結(jié)點結(jié)束,在關(guān)鍵字全集內(nèi)做一次查找,性能逼近二分查找
3、每個節(jié)點最多擁有m個子樹
4、根節(jié)點至少有2個子樹
5、分支節(jié)點至少擁有m/2顆子樹(除根節(jié)點和葉子節(jié)點外都是分支節(jié)點)
6、所有葉子節(jié)點都在同一層、每個節(jié)點最多可以有m-1個key,并且以升序排列
實例圖說明∶
每個節(jié)點占用一個磁盤塊,一個節(jié)點上有兩個升序排序的關(guān)鍵字和三個指向子樹根節(jié)點的指針,指針存儲的是子節(jié)點所在磁盤塊的地址。兩個關(guān)鍵詞劃分成的三個范圍域?qū)?yīng)三個指針指向的子樹的數(shù)據(jù)的范圍域。以根節(jié)點為例,關(guān)鍵字為16和34,P1指針指向的子樹的數(shù)據(jù)范圍為小于16,P2指針指向的子樹的數(shù)據(jù)范圍為16~34 ,P3指針指向的子樹的數(shù)據(jù)范圍為大于34。
查找關(guān)鍵字過程:
缺點:
全值匹配: 全值匹配指的是和索引中的所有列進行匹配
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';
匹配范圍值: 可以查找某一個范圍的數(shù)據(jù)
explain select * from staffs where name > 'Mary';
精確匹配某一列并范圍匹配另外一列:可以查詢第一列的全部和第二列的部分
explain select * from staffs where name = 'July' and age > 25;
只訪問索引的查詢: 查詢的時候只需要訪問索引,不需要訪問數(shù)據(jù)行,本質(zhì)上就是覆蓋索引
explain select name,age,pos from staffs where name = 'July' and age = 25 and pos = 'dev';
基于哈希表的實現(xiàn),只有精確匹配索引所有列的查詢才有效
在mysql中,只有memory的存儲引擎顯式支持哈希索引
哈希索引自身只需存儲對應(yīng)的hash值,所以索引的結(jié)構(gòu)十分緊湊,這讓哈希索引查找的速度非常快
當需要存儲大量的URL,并且根據(jù)URL進行搜索查找,如果使用B+樹,存儲的內(nèi)容就會很大:select id from url where url=""
也可以利用將url使用CRC32做哈希,可以使用以下查詢方式:select id fom url where url="" and url_crc=CRC32("")
此查詢性能較高原因是使用體積很小的索引來完成查找
當包含多個列作為索引,需要注意的是正確的順序依賴于該索引的查詢,同時需要考慮如何更好的滿足排序和分組的需要
案例: 建立組合索引 a,b,c ,不同SQL語句使用索引情況
語句 | 索引是否發(fā)揮作用 |
---|---|
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 |
不是單獨的索引類型,而是一種數(shù)據(jù)存儲方式,指的是數(shù)據(jù)行跟相鄰的鍵值緊湊的存儲在一起,將數(shù)據(jù)存儲與索引放到了一塊,找到索引也就找到了數(shù)據(jù)
如果沒有定義主鍵,InnoDB會選擇一個唯一的非空索引代替。如果沒有唯一索引,InnoDB會隱式定義一個主鍵來作為聚簇索引。InnoDB 只聚集在同一個頁面中的記錄。包含相鄰鍵值的頁面可能相距甚遠。
數(shù)據(jù)文件跟索引文件分開存放,將數(shù)據(jù)存儲于索引分開結(jié)構(gòu),索引結(jié)構(gòu)的葉子節(jié)點指向了數(shù)據(jù)的對應(yīng)行,myisam通過key_buffer把索引先緩存到內(nèi)存中,當需要訪問數(shù)據(jù)時(通過索引訪問數(shù)據(jù)),在內(nèi)存中直接搜索索引,然后通過索引找到磁盤相應(yīng)數(shù)據(jù),這也就是為什么索引不在key buffer命中時,速度慢的原因
通過葉子節(jié)點指針找到數(shù)據(jù)頁中的數(shù)據(jù),所以非聚簇索引是邏輯順序
1、索引條目通常遠小于數(shù)據(jù)行大小,如果只需要讀取索引,那么mysql就會極大的較少數(shù)據(jù)訪問量
2、因為索引是按照列值順序存儲的,所以對于IO密集型的范圍查詢會比隨機從磁盤讀取每一行數(shù)據(jù)的IO要少的多
3、一些存儲引擎如MYISAM在內(nèi)存中只緩存索引,數(shù)據(jù)則依賴于操作系統(tǒng)來緩存,因此要訪問數(shù)據(jù)需要一次系統(tǒng)調(diào)用,這可能會導(dǎo)致嚴重的性能問題
4、由于INNODB的聚簇索引,覆蓋索引對INNODB表特別有用
1、當發(fā)起一個被索引覆蓋的查詢時,在explain的extra列可以看到using index的信息,此時就使用了覆蓋索引
2、在大多數(shù)存儲引擎中,覆蓋索引只能覆蓋那些只訪問索引中部分列的查詢。不過,可以進一步的進行優(yōu)化,可以使用innodb的二級索引來覆蓋查詢。
例如:actor使用innodb存儲引擎,并在last_name字段有二級索引,雖然該索引的列不包括主鍵actor_id,但也能夠用于對actor_id做覆蓋查詢
當使用索引列進行查詢的時候盡量不要使用表達式,把計算放到業(yè)務(wù)層而不是數(shù)據(jù)庫層
盡量使用主鍵查詢,而不是其他索引,因為主鍵查詢不會觸發(fā)回表查詢
使用前綴索引
>有時候需要索引很長的字符串,這會讓索引變的大且慢,通常情況下可以使用某個列開始的部分字符串,這樣大大的節(jié)約索引空間,從而提高索引效率,但這會降低索引的選擇性,索引的選擇性是指不重復(fù)的索引值和數(shù)據(jù)表記錄總數(shù)的比值,范圍從1/#T到1之間。索引的選擇性越高則查詢效率越高,因為選擇性更高的索引可以讓mysql在查找的時候過濾掉更多的行。
一般情況下某個列前綴的選擇性也是足夠高的,足以滿足查詢的性能,但是對應(yīng)BLOB,TEXT,VARCHAR類型的列,必須要使用前綴索引,因為mysql不允許索引這些列的完整長度,使用該方法的訣竅在于要選擇足夠長的前綴以保證較高的選擇性,通過又不能太長。
--創(chuàng)建數(shù)據(jù)表 create table citydemo(city varchar(50) not null); insert into citydemo(city) select city from city; --重復(fù)執(zhí)行5次下面的sql語句 insert into citydemo(city) select city from citydemo; --更新城市表的名稱 update citydemo set city=(select city from city order by rand() limit 1); --查找最常見的城市列表,發(fā)現(xiàn)每個值都出現(xiàn)45-65次, select count(*) as cnt,city from citydemo group by city order by cnt desc limit 10; --查找最頻繁出現(xiàn)的城市前綴,先從3個前綴字母開始,發(fā)現(xiàn)比原來出現(xiàn)的次數(shù)更多,可以分別截取多個字符查看城市出現(xiàn)的次數(shù) 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之后,再增加前綴長度,選擇性提升的幅度已經(jīng)很小了 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; --計算完成之后可以創(chuàng)建前綴索引 alter table citydemo add key(city(7)); --注意:前綴索引是一種能使索引更小更快的有效方法,但是也包含缺點:mysql無法使用前綴索引做order by 和 group by。
使用索引掃描來排序
mysql有兩種方式可以生成有序的結(jié)果:通過排序操作或者按索引順序掃描,如果explain出來的type列的值為index,則說明mysql使用了索引掃描來做排序
掃描索引本身是很快的,因為只需要從一條索引記錄移動到緊接著的下一條記錄。但如果索引不能覆蓋查詢所需的全部列,那么就不得不每掃描一條索引記錄就得回表查詢一次對應(yīng)的行,這基本都是隨機IO,因此按索引順序讀取數(shù)據(jù)的速度通常要比順序地全表掃描慢
mysql可以使用同一個索引即滿足排序,又用于查找行,如果可能的話,設(shè)計索引時應(yīng)該盡可能地同時滿足這兩種任務(wù)。
只有當索引的列順序和order by子句的順序完全一致,并且所有列的排序方式都一樣時,mysql才能夠使用索引來對結(jié)果進行排序,如果查詢需要關(guān)聯(lián)多張表,則只有當orderby子句引用的字段全部為第一張表時,才能使用索引做排序。order by子句和查找型查詢的限制是一樣的,需要滿足索引的最左前綴的要求,否則,mysql都需要執(zhí)行順序操作,而無法利用索引排序
union all,in,or都能夠使用索引,但是推薦使用in
范圍列可以用到索引,范圍條件是:、>,范圍列可以用到索引,但是范圍列后面的列無法用到索引,索引最多用于一個范圍列
強制類型轉(zhuǎn)換會全表掃描
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;(不會觸發(fā)索引) explain select * from user where phone='13800001234';(觸發(fā)索引)
更新十分頻繁,數(shù)據(jù)區(qū)分度不高的字段上不宜建立索引
更新會變更B+樹,更新頻繁的字段建議索引會大大降低數(shù)據(jù)庫性能.
類似于性別這類區(qū)分不大的屬性,建立索引是沒有意義的,不能有效的過濾數(shù)據(jù),
一般區(qū)分度在80%以上的時候就可以建立索引,區(qū)分度可以使用 count(distinct(列名))/count(*) 來計算
創(chuàng)建索引的列,不允許為null,可能會得到不符合預(yù)期的結(jié)果
當需要進行表連接的時候,最好不要超過三張表,因為需要join的字段,數(shù)據(jù)類型必須一致
能使用limit的時候盡量使用limit
單表索引建議控制在5個以內(nèi)
單索引字段數(shù)不允許超過5個(組合索引)
創(chuàng)建索引的時候應(yīng)該避免以下錯誤概念
> 索引越多越好(錯誤)
> 過早優(yōu)化,在不了解系統(tǒng)的情況下進行優(yōu)化(錯誤)
到此這篇關(guān)于MySQL索引知識小妙招的文章就介紹到這了,更多相關(guān)MySQL索引知識內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!