索引是對數據庫表中一列或多列的值進行排序的一種數據結構,使用索引能夠快速訪問數據庫表中的特定信息。mysql
咱們建立索引的時候是這樣的:算法
create index index_name on table_name(column)
能夠這樣想:索引是取出了一列或者幾個特殊的列,而後把他們重命名爲index_name,而後存起來,存的方式是用特殊的數據結構,好比二叉樹。sql
索引中包含了一個表中列的值和它的地址的值,而且這些值存儲在一個數據結構中。索引的組織形式有二叉樹、哈希、B-樹、B+樹。若是索引存的是一列就叫作單索引,多列就是複合索引。數據庫
索引通常以文件形式存在磁盤中(也能夠存於內存),存儲索引的原理大體歸納爲以空間換時間。數據結構
執行這樣一條語句:函數
select name from student where stuno = "003"
在一張學生表裏面找學號是003的學生,假如一共到100號學生。那麼DBMS會生成一個相似指針的東西,從stuno ="001"一直到最後,進行全表掃描。那麼爲何到stuno="003"不中止呢?由於數據庫在未添加索引的時候默認執行的是全量搜索,select執行就就是全表掃描,即便掃描主鍵也同樣。性能
若是咱們在stuno字段上建立了一個索引,假如索引的底層存儲結構是二叉樹,那麼只須要log2(100)次就能查到所須要的數據。所以,創建索引的目的就是加快對錶中記錄的查找或排序。mysql索引
固然索引不是萬能的,它的缺點有:建立和維護索引須要時間和空間成本,每次增刪改索引都須要進行動態維護。優化
索引類型有:主鍵索引,普通索引,複合索引,全文索引,惟一索引搜索引擎
主鍵索引
--建立1 crete table student(id int primary key auto_increment, name varchar(20) not null default) --建立2:先建立表,後建立index alter table student add primary key(id) --在建立主鍵的時候,就會爲咱們建立好主鍵索引。
惟一索引
惟一索引和普通索引相似,主要區別在於,惟一索引限制列的值必須惟一,但容許存在空值(只能有一個)。主鍵索引不容許有空值。
全文索引
在執行模糊查詢的時候,如like "value%"
,這種狀況下,須要考慮使用全文搜索的方式進行優化。全文搜索在MySQL中是一個FULLTEXT類型索引。全文索引主要用來查找文本中的關鍵字,而不是直接與索引中的值進行比較,它更像是一個搜索引擎,而不是簡單的where語句的參數匹配。目前只有char/vachar/text列上能夠建立全文索引,默認Mysql不支持中文全文搜索。Mysql全文搜索只是一個臨時方案,對於全文搜索場景,更專業的作法是使用全文搜索引擎,如ElasticSearch。
select * from user where id=7;
哈希算法首先計算出id=7的無力地址addr=hash(7)=4213,而4213映射的物理地址是0x77,而後就能夠找到咱們想要的數據了。
從時間複雜度來看,哈希算法時間複雜度爲o(1),速度很是快,可是Mysql並無採起哈希做爲其底層算法。由於考慮到數據檢索有一個經常使用的手段就是範圍查找,使用哈希就沒有辦法作到高效的範圍查找。好比:
select * from user where id>3
二叉查找樹的時間複雜度是O(logn),從檢索效率上看是能作到高效檢索,而二叉樹全部左子樹比根節點小,右子樹比根節點大的特性使得它也支持範圍查找。
可是二叉樹有個致命缺點:極端狀況下會退化爲線性表。在數據庫中,數據的自增是一個很常見的形式,好比主鍵id通常默認都是自增的,若是採起二叉樹做爲索引的底層數據結構,那上面介紹的不平衡狀態致使的線形查找問題必然出現!!!所以,簡單的二叉樹不能直接用於實現Mysql底層索引的。
學過數據結構的都知道,解決二叉樹不平衡致使的性能問題,就是二叉平衡樹。經過調整樹的結構,來達到高度的平衡狀態,可是頻繁的調整樹的結構也是一種開銷,而紅黑樹就是捨去一部分的平衡性來得到低的調整開銷。可是紅黑樹也存在「右傾現象」,查找性能又會下降。所以在查找性能和調整樹的開銷之間很難找到一種平衡,也不適合做爲Mysql底層索引結構。
有人會問:數據庫查詢數據的瓶頸在於磁盤IO,調整樹的開銷存在於增刪,而查找不須要調整,爲何AVL樹不適合呢?
答:AVL不是不適合,而是有更好的數據結構。咱們知道磁盤IO有一個特色,就是從磁盤讀取1B的數據和1KB的數據所消耗的時間基本是同樣的,那咱們的優化思路就能夠改成:儘量在一次磁盤 IO 中多讀一點數據到內存。這個直接反映到樹的結構就是,每一個節點能存儲的 key 能夠適當增長,這就是B樹、B+樹的設計原理了。
B樹和B+樹又被稱爲多路查找,一個節點存儲了多個key來減小磁盤IO,從而提升檢索速度。
B樹和B+樹有什麼區別呢?
B樹一個節點裏面存的是(key&value),而B+樹存儲的是(key),因此B樹裏一個節點存不了不少(key&value),可是B+樹一個節點能存儲不少(key),B+樹葉子節點存全部的數據。
B+樹的葉子節點是(key&value,並用一個鏈表串聯起來。
經過 B 樹和 B+樹的對比咱們看出,B+樹節點存儲的是索引,在單個節點存儲容量有限的狀況下,單節點也能存儲大量索引,使得整個 B+樹高度下降,減小了磁盤 IO。其次,B+樹的葉子節點是真正數據存儲的地方,葉子節點用了鏈表鏈接起來,這個鏈表自己就是有序的,在數據範圍查找時,更具有效率。所以 Mysql 的索引用的就是 B+樹,B+樹在查找效率、範圍查找中都有着很是不錯的性能。
Mysql底層數據引擎以插件形式設計,最多見的就是Innodb引擎和Myisam引擎,用戶能夠根據我的需求選擇不一樣的引擎做爲Mysql數據表的底層引擎。咱們剛分析了,B+樹做爲 Mysql 的索引的數據結構很是合適,可是數據和索引到底怎麼組織起來也是須要一番設計,設計理念的不一樣也致使了 Innodb 和 Myisam 的出現,各自呈現獨特的性能。
MyISAM 雖然數據查找性能極佳,可是不支持事務處理。Innodb 最大的特點就是支持了 ACID 兼容的事務功能,並且他支持行級鎖。Mysql 創建表的時候就能夠指定引擎,好比下面的例子,就是分別指定了 Myisam 和 Innodb 做爲 user 表和 user2 表的數據引擎。
create table 'user' ( 'id' int not null default '0' ) engine=myisam default charset=utf8
Innodb建立表後生成的文件有:
Myisam建立表後生成的文件有:
從生成的文件看來,這兩個引擎底層數據和索引的組織方式並不同,MyISAM 引擎把數據和索引分開了,一人一個文件,這叫作非彙集索引方式;Innodb 引擎把數據和索引放在同一個文件裏了,這叫作彙集索引方式。下面將從底層實現角度分析這兩個引擎是怎麼依靠 B+樹這個數據結構來組織引擎實現的。
MyISAM 用的是非彙集索引方式,即數據和索引落在不一樣的兩個文件上。MyISAM 在建表時以主鍵做爲 KEY 來創建主索引 B+樹,樹的葉子節點存的是對應數據的物理地址。咱們拿到這個物理地址後,就能夠到 MyISAM 數據文件中直接定位到具體的數據記錄了。
當咱們爲某個字段添加索引時,咱們一樣會生成對應字段的索引樹,該字段的索引樹的葉子節點一樣是記錄了對應數據的物理地址,而後也是拿着這個物理地址去數據文件裏定位到具體的數據記錄。
InnoDB 是彙集索引方式,所以數據和索引都存儲在同一個文件裏。首先 InnoDB 會根據主鍵 ID 做爲 KEY 創建索引 B+樹,如左下圖所示,而 B+樹的葉子節點存儲的是主鍵 ID 對應的數據,好比在執行 select * from user_info where id=15 這個語句時,InnoDB 就會查詢這顆主鍵 ID 索引 B+樹,找到對應的 user_name='Bob'。
這是建表的時候 InnoDB 就會自動創建好主鍵 ID 索引樹,這也是爲何 Mysql 在建表時要求必須指定主鍵的緣由。當咱們爲表裏某個字段加索引時 InnoDB 會怎麼創建索引樹呢?好比咱們要給 user_name 這個字段加索引,那麼 InnoDB 就會創建 user_name 索引 B+樹,節點裏存的是 user_name 這個 KEY,葉子節點存儲的數據的是主鍵 KEY。注意,葉子存儲的是主鍵 KEY!拿到主鍵 KEY 後,InnoDB 纔會去主鍵索引樹里根據剛在 user_name 索引樹找到的主鍵 KEY 查找到對應的數據。
問題來了,爲何 InnoDB 只在主鍵索引樹的葉子節點存儲了具體數據,可是其餘索引樹卻不存具體數據呢,而要畫蛇添足先找到主鍵,再在主鍵索引樹找到對應的數據呢?
其實很簡單,由於 InnoDB 須要節省存儲空間。一個表裏可能有不少個索引,InnoDB 都會給每一個加了索引的字段生成索引樹,若是每一個字段的索引樹都存儲了具體數據,那麼這個表的索引數據文件就變得很是巨大(數據極度冗餘了)。從節約磁盤空間的角度來講,真的沒有必要每一個字段索引樹都存具體數據,經過這種看似「畫蛇添足」的步驟,在犧牲較少查詢的性能下節省了巨大的磁盤空間,這是很是有值得的。
在進行 InnoDB 和 MyISAM 特色對比時談到,MyISAM 查詢性能更好,從上面索引文件數據文件的設計來看也能夠看出緣由:MyISAM 直接找到物理地址後就能夠直接定位到數據記錄,可是 InnoDB 查詢到葉子節點後,還須要再查詢一次主鍵索引樹,才能夠定位到具體數據。等於 MyISAM 一步就查到了數據,可是 InnoDB 要兩步,那固然 MyISAM 查詢性能更高。
複合索引:選擇索引列的順序
1)儘可能把字段長度小的列放在聯合索引的最左側(由於字段長度越小,一頁能存儲的數據量越大,IO性能也就越好) 2)區分度最高的放在聯合索引的最左側(區分度=列中不一樣值的數量/列的總行數) 3)使用最頻繁的列放到聯合索引的左側(這樣能夠比較少的創建一些索引)
表關聯查詢
1)類型和大小要相同,可使用索引。 VARCHAR(10)和 CHAR(10)大小相同,但 VARCHAR(10)與 CHAR(15)不相同。 2)字符串列之間比較,兩列應使用相同的字符集。例如,將utf8列與 latin1列進行比較會不使用索引。 3)將字符串列與時間或數字列進行比較時,在沒有轉換狀況下,不使用索引。