MySQL索引、算法(基于InnoDB引擎)和優化

索引

索引是應用程序設計和開發的一個重要方面。如果索引太多,應用的性能可能會受到影響;如果索引太少,對查詢性能又會產生影響。

1 innodb存儲引擎介紹

innodb存儲引擎支持兩種常見的索引:B+樹索引哈希索引

innodb支持哈希索引是自適應的,innodb會根據表的使用情況自動生成哈希索引。

B+樹索引就是傳統意義上的索引,是關系型數據庫中最常用最有效的索引。B+樹是從最早的平衡二叉樹演變而來,但是B+樹不是一個二叉樹。B+中的B不代表二叉(Binary),而是代表平衡(Balance)。

注意:

B+樹索引并不能找到一個鍵值對應的具體行。b+樹索引只能查到被查找數據行所在的頁,然后數據庫通過把頁讀入內存,再在內存中查找,最后得到結果。

2 B+樹索引介紹

B+樹索引的本質是B+樹在數據庫中的實現。但是B+樹索引有一個特點是高扇出性,因此在數據庫中,B+樹的高度一般在2到3層。也就是說查找某一鍵值的記錄,最多只需要2到3次IO開銷。按磁盤每秒100次IO來計算,查詢時間只需0.0.2到0.03秒。

數據庫中B+樹索引分為聚集索引(clustered index)非聚集索引(secondary index),這兩種索引的共同點是內部都是B+樹,高度都是平衡的,葉節點存放著所有數據。不同點是葉節點是否存放著一整行數據。

1. 聚集索引
Innodb存儲引擎表是索引組織表,即表中數據按主鍵順序存放。而聚集索引就是按每張表的主鍵構造一顆B+樹。并且葉節點存放整張表的行記錄數據。每張表只能有一個聚集索引(一個主鍵)。
聚集索引的另一個好處是它對于主鍵的排序查找和范圍的速度非常快。葉節點的數據就是我們要找的數據。

2. 輔助索引
輔助索引(也稱非聚集索引)。葉級別不包含行的全部數據,葉級別除了包含行的鍵值以外,每個索引行還包含了一個書簽(bookmark),該書簽告訴innodb存儲引擎,哪里可以找到與索引對應的數據。
輔助索引的存在并不影響數據再聚集索引中的組織,因此一個表可以有多個輔助索引。當通過輔助索引查找數據時,innodb會遍歷輔助索引并通過葉級別的指針獲得指向主鍵索引的主鍵。然后再通過主鍵索引找到一行完整的數據

3 使用場景

  1. 快速查找符合where條件的記錄
  2. 快速確定候選集。若where條件使用了多個索引字段,則MySQL會優先使用能使候選記錄集規模最小的那個索引,以便盡快淘汰不符合條件的記錄。
  3. 如果表中存在幾個字段構成的聯合索引,則查找記錄時,這個聯合索引的最左前綴匹配字段也會被自動作為索引來加速查找。例如,若為某表創建了3個字段(c1, c2, c3)構成的聯合索引,則(c1), (c1, c2), (c1, c2, c3)均會作為索引,(c2, c3)就不會被作為索引,而(c1, c3)其實只利用到c1索引。
  4. 多表做join操作時會使用索引(如果參與join的字段在這些表中均建立了索引的話)
  5. 若某字段已建立索引,求該字段的min()或max()時,MySQL會使用索引
  6. 對建立了索引的字段做sort或group操作時,MySQL會使用索引

4 建立、修改索引

1 索引創建:兩種方式

ALTER [ONLINE | OFFLINE] [IGNORE] TABLE tbl_name| ADD {INDEX|KEY} [index_name]   [index_type] (index_col_name,...) [index_option]

CREATE [ONLINE|OFFLINE] [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name [index_type] ON tbl_name (index_col_name,...)

2 索引刪除:兩種方式

ALTER [ONLINE | OFFLINE] [IGNORE] TABLE tbl_name | DROP PRIMARY KEY | DROP {INDEX|KEY} index_names

DROP [ONLINE|OFFLINE] INDEX index_name ON tbl_name

算法(B+樹)

B+樹是為磁盤及其他存儲輔助設備而設計一種平衡查找樹(不是二叉樹)。B+樹中,所有記錄的節點按大小順序存放在同一層的葉節點中,各葉節點用指針進行連接。

下面演示一個B+數結構,高度為2,每頁可放4條記錄,扇出(fan out)為5。從下圖1可以看出,所有記錄都在頁節點中,并且為順序存放,我們從最左邊的葉節點開始遍歷,可以得到所有鍵值的順序排序:5、10、15、20、25、30、50、55、60、65、75、80、85、90.

  1. B+樹的插入操作
    B+樹的插入必須保證插入后葉節點的記錄依然排序。同時要考慮插入B+樹的三種情況,每種情況都可能導致不同的插入算法。如下表所示:


    我們實例分析B+樹的插入,在圖1的B+樹中,我們需要插入28這個值。因為Leaf Page和Index page都沒有滿,我們直接將記錄插入葉節點就可以了。如下圖2所示:
    圖2 插入鍵值28

    下面我們再插入70這個值,這時Leaf Page已經滿了,但是Index Page還沒有滿,符合上面的第二種情況。這時插入Leaf Page的情況為50、55、60、65、70.我們根據中間的值60拆分葉節點,可得到下圖3所示(雙項鏈表指針依然存在,沒有畫出):
    圖3 插入鍵值70
    最后我們再插入95,這個Leaf Page和Index Page都滿了,符合上面第三種情況。需要做2次拆分,如下圖4所示:
    圖4 插入鍵值95

    可以看到,不管怎么變化,B+樹總會保持平衡。但是為了保持平衡,對于新插入的鍵值可能需要做大量的拆分頁操作。B+樹主要用于磁盤,拆分意味著磁盤的操作,應該在可能的情況下盡量減少頁的拆分。因此,B+樹提供了旋轉功能。旋轉發生在Leaf Page已經滿了,但是左右兄弟節點沒有滿的情況下。這時B+樹并不是急著做頁的拆分,而是旋轉。旋轉結果如圖5所示,可以看到旋轉操作使B+樹減少了一次頁的拆分操作,高度仍然為2.
    圖5 B+樹的旋轉操作

  2. B+樹的刪除操作
    B+樹使用填充因子來控制數的刪除變化。填充因子可以設置的最小值為50%。B+樹的刪除操作同樣保證刪除后葉節點的記錄依然排序。
    根據填充因子的變化,B+樹刪除依然需要考慮三種情況,如下表所示:

根據圖4的B+樹,我們進行刪除操作,首先刪除鍵值為70的這條記錄,該記錄符合上表第一種情況,刪除后如下圖6所示:


接著我們刪除鍵值為25的記錄,這也是屬于上表第一種情況,不同的是該值還是index page中的值。因此在刪除Leaf Page中的25后,還需要將25的右兄弟節點28更新到Index Page中,如下圖7所示(圖中有兩個筆誤,紅色為修正值):
圖7 刪除鍵值28
最后我們刪除鍵值為60的記錄。刪除Leaf page鍵值為60的記錄后,其填充因子小于50%。需要做合并操作。同樣在刪除Index page中相關記錄后需要做Index Page的合并操作。

優化

MySQL數據庫是常見的兩個瓶頸是CPU和I/O的瓶頸,CPU在飽和的時候一般發生在數據裝入內存或從磁盤上讀取數據時候。磁盤I/O瓶頸發生在裝入數據遠大于內存容量的時候,如果應用分布在網絡上,那么查詢量相當大的時候那么平瓶頸就會出現在網絡上,我們可以用mpstat, iostat, sar和vmstat來查看系統的性能狀態。

除了服務器硬件的性能瓶頸,對于MySQL系統本身,我們可以使用工具來優化數據庫的性能,通常有三種:使用索引,使用EXPLAIN分析查詢以及調整MySQL的內部配置

1 性能分析工具

show profile;
mysqlsla;   // 壓測
mysqldumpslow;  // 慢查詢slow sql
explain;
show slow log;
show processlist;
show query_response_time(percona);

2 Explain

explain select count(*) from XXX;

+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | **rows** | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | XXX | ALL | NULL | NULL | NULL | NULL | 6897 | NULL |+----+-------------+---------+------+---------------+------+---------+------+------+-------+

EXPLAIN字段:

  1. id:本次 select 的標識符。在查詢中每個 select都有一個順序的數值。
  2. select_type:
  1. SIMPLE:查詢中不包含子查詢或者UNION
  2. PRIMARY:查詢中若包含任何復雜的子部分,最外層查詢則被標記為
  3. SUBQUERY:在SELECT或WHERE列表中包含了子查詢,該子查詢被標記為
  4. DERIVED(衍生):在FROM列表中包含的子查詢被標記為
  5. UNION: 若第二個SELECT出現在UNION之后,則被標記為UNION;若UNION包含在 FROM子句的子查詢中,外層SELECT將被標記為:DERIVED
  6. 從UNION表獲取結果的SELECT被標記為:UNION RESULT
  1. Table:顯示這一行的數據是關于哪張表的

  2. possible_keys:顯示可能應用在這張表中的索引。如果為空,沒有可能的索引。可以為相關的域從WHERE語句中選擇一個合適的語句

  3. key:實際使用的索引。如果為NULL,則沒有使用索引。MYSQL很少會選擇優化不足的索引,此時可以在SELECT語句中使用USE INDEX(index)來強制使用一個索引或者用IGNORE INDEX(index)來強制忽略索引

  4. key_len:使用的索引的長度。在不損失精確性的情況下,長度越短越好

  5. ref:顯示索引的哪一列被使用了,如果可能的話,是一個常數

  6. rows:MySQL認為必須檢索的用來返回請求數據的行數

  7. type:這是最重要的字段之一,顯示查詢使用了何種類型。從最好到最差的連接類型為systemconsteq_regrefrangeindexALL

system、const:可以將查詢的變量轉為常量. 如id=1; id為 主鍵或唯一鍵.

eq_ref:訪問索引,返回某單一行的數據.(通常在聯接時出現,查詢使用的索引為主鍵或惟一鍵)

ref:訪問索引,返回某個值的數據.(可以返回多行) 通常使用=時發生

range:這個連接類型使用索引返回一個范圍中的行,比如使用>或<查找東西,并且該字段上建有索引時發生的情況(注:不一定好于index)

index:以索引的順序進行全表掃描,優點是不用排序,缺點是還要全表掃描

ALL:全表掃描,應該盡量避免

  1. Extra:關于MYSQL如何解析查詢的額外信息。
    主要有以下幾種:
  1. using index:只用到索引,可以避免訪問表.

  2. using where:使用到where來過慮數據. 不是所有的where clause都要顯示using where. 如以=方式訪問索引.

  3. using tmporary:用到臨時表

  4. using filesort:用到額外的排序. (當使用order by v1,而沒用到索引時,就會使用額外的排序)

  5. range checked for eache record(index map:N):沒有好的索引.


----
參考
- MySQL技術內幕I弄DB存儲引擎
- http://www.oicto.com/mysql-explain-show/
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內容