天天看點

資料庫學習篇之資料庫索引深入了解

索引的存儲

聚集索引和非聚集索引的存儲不相同,那麼都是怎麼存儲的呢?

例如:有一張學生表如下:

create table 'student' (
'id' int(11) not null auto_increment comment '主鍵id',
'name' varchar(50) not null default '' comment '學生姓名',
'age' int(11) not null default 0 comment '學生年齡',
primary key ('id'),
key 'idx_age' ('age'),
key 'idx_name' ('name')
) ENGINE=InnoDB default charset=utf8 comment ='學生資訊';
           

 表中具體資料如下:

資料庫學習篇之資料庫索引深入了解

id 為主鍵索引,name和age為非聚集索引

1.聚集索引在磁盤中的存儲

資料庫學習篇之資料庫索引深入了解

聚集索引葉子結點存儲是表裡面的所有行資料;每個資料頁在不同的磁盤上面;

如果要查找id=5的資料,那麼先把磁盤0讀入記憶體,然後用二分法查找id=5的數在3和6之間,然後通過指針p1查找到磁盤2的位址,然後将磁盤2讀入記憶體中,用二分查找方式查找到id=5的資料。

2.非聚集索引在磁盤中的存儲

資料庫學習篇之資料庫索引深入了解

葉子結點存儲的是聚集索引鍵,而不存儲表裡面所有的行資料,是以在查找的時候,隻能查找到聚集索引鍵,再通過聚集索引去表裡面查找到資料。

如果要查找到name = 小徐,首先将磁盤0加載到記憶體中,然後用二分查找的方法查到在指針p1所指的位址上,然後通過指針p1所指的位址可知道在磁盤2上面,然後通過二分查找法得知小徐id=4;

然後在根據id=4将磁盤0加載到記憶體中,然後通過二分查找的方法查到在指針p1所指的位址上,然後通過指針p1所指的位址可知道在磁盤2上面,然後通過id=4查找出需要的行資料,就查找出name=小徐的資料了。

索引失效的情況---哪些情況sql不走索引

根據上例中的學生資訊表,表中兩個字段age,name加了索引,索引失效情況具體如下:

key 'idx_age' ('age'),

key 'idx_name' ('name')
           

  1.Like這種就是%在前面的不走索引,在後面的走索引

A:select * from student where'name' like'王%'

B:select * from student where'name' like'%小'

A走索引,B不走索引
           

  2.用索引列進行計算的,不走索引

A:select * from student where age =10+8

B:select * from student where age +8 =18

A走索引,B不走索引
           

 3.對索引列用函數了,不走索引

A:select * from student where  concat('name','哈') ='王哈哈';

B:select * from student where name = concat('王哈','哈');

A不走索引,B走索引
           

 4. 索引列用了!= 不走索引

select * from student where age !=18
           

總的來說,哪些情況索引會失效有以下幾種:

(1)條件中用or(這就是為什麼少用or的原因)

(2)對于多列(複合、聯合)索引,不是使用的第一部分,則不會使用索引。(最左比對原則或者叫做最左字首原則)

比如:Index_SoftWareDetail索引包含(a,b,c) 三列,但是查詢條件裡面,沒有a,b 列,隻有c 列,那麼 Index_SoftWareDetail索引也不起作用。

是以:bc   c   acb bac 都是不行的
           

(3)like的模糊查詢以%開頭,索引失效

(4)如果列類型是字元串,那一定要在條件中将資料使用引号引用起來,否則不會使用索引

(5)如果MySQL預計使用全表掃描要比使用索引快,則不使用索引

(6)判斷索引列是否不等于某個值時,‘!=’操作符

(7)對索引列進行運算。這裡運算包括+-*/等運算,也包括使用函數

(8)索引字段進行判空查詢時。也就是對索引字段判斷是否為NULL時。語句為is null 或is not null。

比如:select * from SoftWareDetailInfo where CreateTime is null 此時就不檢索time字段上的索引表了。也就是索引在這條語句執行時失效了。
接着再執行:
select * from SoftWareDetailInfo where CreateTime = '2015-04-11 00:00:00' 此時就會檢索索引表了。索引又起作用了。
           

(9)範圍列可以用到索引(聯合索引必須是最左字首),但是範圍列後面的列無法用到索引

索引的優化

(1)建立聯合索引的時候,要把查詢頻率較高的列放在最左邊

(2)如果建立了(a,b)索引,就不必再獨立建立a索引。同理如果建立了(a,b,c)聯合索引,就不必再獨立建立a,(a,b)索引

(3)存在非等号和等号混合判斷條件時,在建索引時,請把等号條件的列前置。如where a>? and b=?,那麼即使 a 的區分度更高,也必須把 b 放在索引的最前列。

(4)最左字首原則,并不是要求where後的順序和聯合索引的一緻。下面的 SQL 語句也可以命中 (login_name, passwd) 這個聯合索引。但還是建議 where 後的順序和聯合索引一緻,養成好習慣。

select uid, login_time from user where passwd=? and login_name=?
           

(5)盡量不要使用左模糊和全模糊,如果需要可以使用搜尋引擎來解決。

(6)union,in和or都可以命中索引,但是建議使用in。

(7)負向條件查詢不能使用索引,可以優化為in查詢。

負向條件查詢有:!=  <>  not in  not like等等

例如:select * from user where status!=1 and status!=2

優化為:select * from user where status in (0,3,4);
           

(8)合理使用聯合索引的最左字首原則,如果在(a,b,c)三個字段上建立聯合索引,那麼它能夠加快 a | (a,b) | (a,b,c) 三組查詢速度。

比如說把(username,password)建立了聯合索引,因為業務上幾乎沒有password的單條件查詢,而有很多username的單條件查詢需求,是以應該建立(username,password)的聯合索引,而不要建立(password,username)的聯合索引
           

(9)把計算放到業務層而不是資料庫層。

(10)表資料比較少、更新十分頻繁、資料區分度不高的字段上不宜建立索引。

一般區分度在80%以上的時候就可以建立索引,區分度可以使用 count(distinct(列名))/count(*) 來計算。
           

(11)強制類型轉換會全表掃描。

例如:如果phone字段是varchar類型,則下面的sql不能命中索引

select * from user where phone = 18838003017

可以優化為:select * from user where phone = '18838003017'
           

(12)利用覆寫索引進行查詢操作,避免回表,提高效率。

select uid,login_time from user where username=? and password=?

如果建立了(username,password,login_time)的聯合索引,由于login_time已經建立在索引中了,被查詢的username和password就不用去row上擷取資料了,進而加速查詢
           

(13)在order by和group by中要注意索引的有序性。

如果order by是組合索引的一部分,應該将該字段放在組合索引的最後

例如:where a=? and b=? order by c ->可以建立聯合索引(a,b,c)

如果索引中有範圍查找,則索引的有序性無法利用

例如:where a>10 order by b ->索引(a,b)無法排序
           

(14)建立索引的列,不許為null。

單列索引不存 null 值,複合索引不存全為 null 的值,如果列允許為 null,可能會得到“不符合預期”的結果集,是以,請使用 not null 限制以及預設值。
           

sql語句的優化

(1)能用到索引盡量用到索引,對索引的優化實際上就是sql語句的調優。

(2)任何地方都不要使用 select * from t ,用具體的字段清單代替“*”,不要傳回用不到的任何字段。

(3)盡量使用where,而不要使用having。

(4)盡量使用多表查詢,不要使用子查詢。

(5)where後的and,or左右執行順序不同。

運算符為and時--盡量把為假的放在右邊

運算符為or時--盡量把為真的放在右邊
           
PS:本篇為網上查找的教程學習整理形成,原文連結找不到了,侵删。