索引
索引是幫助MySQL高效獲取數據的數據結構。筆者理解索引相當于一本書的目錄,通過目錄就知道要的資料在哪里,不用一頁一頁查閱找出需要的資料。關鍵字index
索引分類
唯一索引
強調唯一,就是索引值必須唯一,關鍵字unique index。
創建索引:
create unique index 索引名 on 表名(列名);
alter table 表名 add unique index 索引名 (列名);
刪除索引:
drop index 索引名 on 表名;
alter table 表名 drop index 索引名;
主鍵
主鍵就是唯一索引的一種,主鍵要求建表時指定,一般用auto_increatment列,關鍵字是primarykey。
主鍵創建:
creat table test (id int not null primary key auto_increment);
全文索引
InnoDB不支持,Myisam支持性能比較好,一般在 CHAR、VARCHAR 或 TEXT 列上創建。
單列索引與多列索引
索引可以是單列索引也可以是多列索引(也叫復合索引)。按照上面形式創建出來的索引是單列索引,
創建多列索引:
create table test (id int not null primary key auto_increment,uname char
(8) not null default ”,password char(12) not null,INDEX(uname,password))type
=myisam;
注意:INDEX(a, b, c)可以當做a或(a, b)的索引來使用,但和b、c或(b,c)的索引來使用這是一個最左前綴的優化方法,在后面會有詳細的介紹,你只要知道有這樣兩個概念
查看表的索引
命令:show index from 表名
Table:表名
Key_name:什么類型索引(這了是主鍵)
Column_name:索引列的字段名
Cardinality:索引基數,很關鍵的一個參數,平均數值組=索引基數/表總數據行,平均數值組越接近1就越有可能利用索引
Index_type:如果索引是全文索引,則是fulltext,這里是b+tree索引
InnoDB索引實現
B-樹
性質:是一種多路搜索樹(并不是二叉的):
定義任意非葉子結點最多只有M個兒子;且M>2;
根結點的兒子數為[2, M];
除根結點以外的非葉子結點的兒子數為[M/2, M];
每個結點存放至少M/2-1(取上整)和至多M-1個關鍵字;(至少2個關鍵字)
非葉子結點的關鍵字個數=指向兒子的指針個數-1;
-
所有葉子結點位于同一層;
搜索:從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,如果命中則結束,否則進入查詢關鍵字所屬范圍的兒子結點;重復,直到所對應的兒子指針為空,或已經是葉子結點;
B-樹 M=3
B+樹
性質:B+樹是B-樹的變體,也是一種多路搜索樹:
其定義基本與B-樹同,除了:
非葉子結點的子樹指針與關鍵字個數相同;
非葉子結點的子樹指針P[i],指向關鍵字值屬于[K[i], K[i+1])的子樹(B-樹是開區間);
為所有葉子結點增加一個鏈指針;
-
所有關鍵字都在葉子結點出現;
B+樹 M=3
綜上所述,用B-Tree作為索引結構效率是非常高的。
為什么使用B+樹
為什么使用B-/+樹
文件很大,不可能全部存儲在內存中,故要存儲到磁盤上。
索引的結構組織要盡量減少查找過程中磁盤I/O的存取次數。
局部性原理與磁盤預讀,預讀的長度一般為頁(page)的整倍數。
數據庫系統巧妙利用了磁盤預讀原理,將一個節點的大小設為等于一個頁,這樣每個節點只需要一次I/O就可以完全載入。
為什么使用B+樹
B+樹更適合外部存儲,由于內節點無 data 域,一個結點可以存儲更多的內結點,每個節點能索引的范圍更大更精確,也意味著 B+樹單次磁盤IO的信息量大于B-樹,I/O效率更高。
Mysql是一種關系型數據庫,區間訪問是常見的一種情況,B+樹葉節點增加的鏈指針,加強了區間訪問性,可使用在范圍區間查詢等,而B-樹每個節點 key 和 data 在一起,則無法區間查找。
索引使用的時候注意事項
索引不會包含有NULL值的列(MySQL5.0已經優化)。只要列中包含有NULL值都將不會被包含在MySQL索引中,復合索引中只要有一列含有NULL值,那么這一列對于此復合索引就是無效的。所以我們在數據庫設計時不要讓字段的默認值為NULL。
使用短索引。對串列進行索引,如果可能應該指定一個前綴長度。例如,如果有一個CHAR(255)的列,如果在前10個或20個字符內,多數值是惟一的,那么就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作。
索引列排序(單獨order by 用不了索引,索引考慮加where 或加limit) 。MySQL查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那么order by中的列是不會使用索引的。
like語句操作 。一般情況下不鼓勵使用like操作,如果非使用不可,如何使用也是一個問題。like “%aaa%” 不會使用MySQL索引而like “aaa%”可以使用索引。
OR 其中有一個不是索引會遍歷全表
最左前綴匹配原則,非常重要的原則,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的順序可以任意調整。
=和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式。
盡量選擇區分度高的列作為索引,區分度的公式是count(distinct col)/count(*),表示字段不重復的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0,那可能有人會問,這個比例有什么經驗值嗎?使用場景不同,這個值也很難確定,一般需要join的字段我們都要求是0.1以上,即平均1條掃描10條記錄。
索引列不能參與計算,保持列“干凈”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’)。
盡量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那么只需要修改原來的索引即可
索引使用的時候注意事項
- NULL 其實并不是空值,而是要占用空間,建議使用 NOT NULL 而不是 NULL