雖然InnoDB也使用B+Tree做爲索引結構,但具體實現方式卻與MyISAM大相徑庭。由於InnoDB支持聚簇索引(主鍵索引),聚簇索引就是表,因此InnoDB不用像MyISAM那樣須要獨立的行存儲。也就是說,InnoDB的數據文件自己就是索引文件。數據庫
聚簇索引的每個葉子節點都包含了主鍵值、事務ID、用於事務和MVCC的回滾指針以及全部的剩餘列。假設咱們以col1爲主鍵,則下圖是一個InnoDB表的聚簇索引(主鍵索引)(Primary key)示意。服務器
與MyISAM不一樣的是,InnoDB的二級索引和聚簇索引很不相同。InnoDB的二級索引的葉子節點存儲的不是行號(行指針),而是主鍵列。這種策略的缺點是二級索引須要兩次索引查找,第一次在二級索引中查找主鍵,第二次在聚簇索引中經過主鍵查找須要的數據行。併發
畫外音:能夠經過咱們前面提到過的索引覆蓋來避免回表查詢,這樣就只須要一次回表查詢,對於InnoDB而言,就是隻須要一次索引查找就能夠查詢到須要的數據記錄,由於須要的數據記錄已經被索引到二級索引中,直接就能夠找到。高併發
由於InnoDB的索引的方式經過主鍵彙集數據,嚴重依賴主鍵。索引若是沒有定義主鍵,那麼InnoDB會選擇一個惟一的非空索引代替。若是沒有這樣的索引,InnoDB會隱式定義一個主鍵來做爲聚簇索引。性能
畫外音:關於頁,咱們在上一篇文章中也提到過。頁是計算機管理存儲器的邏輯塊,硬件及操做系統每每將主存和磁盤存儲區分割爲連續的 大小相等的塊,每一個存儲塊稱爲一頁。存和磁盤以頁爲單位交換數據。數據庫系統的設計者巧妙利用了磁盤預讀原理,將一個節點的大小設 爲等於一個頁,這樣每一個節點只須要一次磁盤I/O就能夠徹底載入
基於聚簇索引以上的這些特色,在InnoDB中,咱們應該儘可能使用和應用無關的主鍵,例如自增主鍵,這樣能夠保證數據行是按照順序寫入的。而不是使用GUID、UUID生成隨機的主鍵。索引分裂我的理解:在 MySQL插入記錄的同時會更新配置的相應索引文件,根據以上的瞭解,在插入索引時,可能會存在索引的頁的分裂,所以會致使磁盤數據的移動。當插入的主鍵是隨機字符串時,每次插入不會是在B+樹的最後插入,每次插入位置都是隨機的,每次均可能致使數據頁的移動,並且字符串的存儲空間佔用也很大,這樣重建索引不只僅效率低並且 MySQL的負載也會很高,同時還會致使大量的磁盤碎片,磁盤碎片多了也會對查詢形成必定的性能開銷,由於存儲位置不連續致使更多的磁盤I/O,這就是爲何推薦定義主鍵爲遞增整型的一個緣由優化
自增主鍵的弊端 對於高併發的場景,在InnoDB中按照主鍵的順序插入可能會形成明顯的爭用,主鍵的上界會成爲「熱點」,由於全部的插入都發生在此處,索引併發的插入可能會形成間隙鎖競爭,何爲間隙鎖競爭,下個會詳細介紹;另一個緣由多是Auto_increment的鎖機制,在 MySQL處理自增主鍵時,當innodb_autoinc_lock_mode爲0或1時,在不知道插入有多少行時,好比insert t1 xx select xx from t2,對於這個statement的執行會進行鎖表,只有這個statement執行完之後纔會釋放鎖,而後別的插入纔可以繼續執行,可是在innodb_autoinc_lock_mode=2時,這種狀況不會存在表鎖,可是隻能保證全部併發執行的statement插入的記錄是惟一而且自增的,可是每一個statement作的多行插入之間是不鏈接的spa
優化器不使用索引選擇全表掃描 好比一張order表中有聯合索引(order_id, goods_id),在此例子上來講明這個問題是從兩個方面來講:操作系統
select order_id from order where order_id > 1000; --若是查看其執行計劃的話,發現是用use index condition,走的是索引覆蓋。
select * from order where order_id > 1000;
此條語句查詢的是該表全部字段,有一部分字段並未在此聯合索引中,所以走聯合索引查詢會走兩步,首先經過聯合索引肯定符合條件的主鍵id,而後利用這些主鍵id再去聚簇索引中去查詢,而後獲得全部記錄,利用主鍵id在聚簇索引中查詢記錄的過程是無序的,在磁盤上就變成了離散讀取的操做,假如當讀取的記錄不少時(通常是整個表的20%左右),這個時候優化器會選擇直接使用聚簇索引,也就是掃全表,由於順序讀取要快於離散讀取,這也就是爲什麼通常不用區分度不大的字段單獨作索引,注意是單獨由於利用此字段查出來的數據會不少,有很大機率走全表掃描。設計
範圍查詢以後的條件不走索引 根據 MySQL的查詢原理的話,當處理到where的範圍查詢條件後,會將查詢到的行所有返回到服務器端(查詢執行引擎),接下來的條件操做在服務器端進行處理,這也就是爲何範圍條件不走索引的緣由了,由於以後的條件過濾已經不在存儲引擎完成了。可是在 MySQL 5.6之後假如了一個新的功能index condition pushdown(ICP),這個功能容許範圍查詢條件以後的條件繼續走索引,可是須要有幾個前提條件:指針
分頁offset值很大性能問題
在 MySQL中,分頁當offset值很大的時候,性能會很是的差,好比limit 100000, 20,須要查詢100020條數據,而後取20條,拋棄前100000條,在這個過程當中產生了大量的隨機I/O,這是性能不好的緣由,爲了解決這個問題,切入點即是減小無用數據的查詢,減小隨機I/O
select * from t1 inner join (select id from t1 where xxx order by xx limit 1000000,5) as t2 using(id); --子查詢先走索引覆蓋查得id,而後根據獲得的id直接取5條得數據。
select * from t1 where id > 1000000 order by id limit 0, 5; --即利用條件id > 1000000在掃描索引是跳過1000000條記錄,而後取5條便可,這種處理方式的offset值便成爲0了,但此種方式一般分頁不能用,可是能夠用來分批取數據。