第16期:索引設計(MySQL 的索引結構)

image

上一章講了數據庫基本上都用 B+ 樹來存儲索引的緣由:適合磁盤存儲,可以充分利用多叉平衡樹的特性,磁盤預讀,而且很好的支持等值,範圍,順序掃描等。這篇主要介紹 MySQL 兩種經常使用引擎,MyISAM 和 InnoDB 的索引組織方式,瞭解這些存儲方式,對數據庫優化頗有幫助。mysql

MySQL 的索引按照存儲方式分爲兩類:

彙集索引:也稱 Clustered Index。是指關係表記錄的物理順序與索引的邏輯順序相同。因爲一張表只能按照一種物理順序存放,一張表最多也只能存在一個彙集索引。與非彙集索引相比,彙集索引有着更快的檢索速度。sql

MySQL 裏只有 INNODB 表支持彙集索引,INNODB 表數據自己就是彙集索引,也就是常說 IOT,索引組織表。非葉子節點按照主鍵順序存放,葉子節點存放主鍵以及對應的行記錄。因此對 INNODB 表進行全表順序掃描會很是快。數據庫

非彙集索引:也叫 Secondary Index。指的是非葉子節點按照索引的鍵值順序存放,葉子節點存放索引鍵值以及對應的主鍵鍵值。MySQL 裏除了 INNODB 表主鍵外,其餘的都是二級索引。MYISAM,memory 等引擎的表索引都是非彙集索引。簡單點說,就是索引與行數據分開存儲。一張表能夠有多個二級索引。 優化

MYISAM 表:

MYISAM 表是典型的數據與索引分離存儲,主鍵和二級索引沒有本質區別。好比在 MYISAM 表裏主鍵、惟一索引是同樣的,沒有本質區別。spa

假設表 t1 爲 MYISAM 引擎,列爲 ID,姓名,性別,年齡,手機號碼。其中 ID 爲主鍵,年齡爲二級索引。記錄以下:設計

image

那對應的兩個 B+ 樹索引以下圖所示,3d

主鍵字段索引樹:

image

上圖是一個 3 階的 B+ 樹,非葉子節點按照主鍵的值排序存儲,葉子節點一樣按照主鍵的值排序存儲,而且包含指向磁盤上的物理數據行指針。 指針

年齡字段索引樹:

image

上圖年齡字段索引樹一樣是一個 3 階的 B+ 樹,非葉子節點按照年齡字段的值順序存儲,葉子節點保存年齡字段的值以及指向磁盤上的物理數據行指針。code

從上面兩張圖能夠看出,MYISAM 表的索引存儲方式最大的缺點沒有按照物理數據行順序存儲,這樣不管對主鍵的檢索仍是對二級索引的檢索都須要進行二次排序。 blog

舉個簡單例子演示下,

如下 SQL 1 默認沒有排序,亂序輸出;須要按照 ID 順序輸出,就得用 SQL 2,顯式加 ORDER BY 。

mysql
# SQL 1
mysql> select * from t1;
+-------+----------+--------+------+--------------+
| id    | username | gender | age  | phone_number |
+-------+----------+--------+------+--------------+
| 10001 | 小花     | 女     |   18 | 18501877098  |
| 10005 | 小李     | 女     |   21 | 15827654555  |
| 10006 | 小白     | 男     |   38 | 19929933000  |
| 10009 | 小何     | 男     |   35 | 19012378676  |
| 10002 | 小王     | 男     |   20 | 17760500293  |
| 10003 | 小趙     | 女     |   29 | 13581386000  |
| 10004 | 小青     | 女     |   25 | 13456712000  |
| 10007 | 小米     | 男     |   23 | 19800092354  |
| 10008 | 小徐     | 女     |   22 | 18953209331  |
+-------+----------+--------+------+--------------+
9 rows in set (0.00 sec)

# SQL 2
mysql> select * from t1 order by id;
+-------+----------+--------+------+--------------+
| id    | username | gender | age  | phone_number |
+-------+----------+--------+------+--------------+
| 10001 | 小花     | 女     |   18 | 18501877098  |
| 10002 | 小王     | 男     |   20 | 17760500293  |
| 10003 | 小趙     | 女     |   29 | 13581386000  |
| 10004 | 小青     | 女     |   25 | 13456712000  |
| 10005 | 小李     | 女     |   21 | 15827654555  |
| 10006 | 小白     | 男     |   38 | 19929933000  |
| 10007 | 小米     | 男     |   23 | 19800092354  |
| 10008 | 小徐     | 女     |   22 | 18953209331  |
| 10009 | 小何     | 男     |   35 | 19012378676  |
+-------+----------+--------+------+--------------+
9 rows in set (0.00 sec)

接下來看看 INNODB 的主鍵索引和二級索引的組成方式。

INNODB 表:

INNODB 表自己是索引組織表,也就是說索引就是數據。下圖表T1的數據行以聚簇索引的方式展現,非葉子節點保存了主鍵的值,葉子節點保存了主鍵的值以及對應的數據行,而且每一個頁有分別指向先後兩頁的指針。

INNODB 表不一樣於 MYISAM,INNODB 表有本身的數據頁管理,默認 16KB。MYISAM 表數據的管理依賴文件系統,好比文件系統通常默認 4KB,MYISAM 的塊大小也是 4KB,MYISAM 表的沒有本身的一套崩潰恢復機制,所有依賴於文件系統。

image

INNODB 表這樣設計的優勢有兩個:

  1. 數據按照主鍵順序存儲。主鍵的順序也就是記錄行的物理順序,相比指向數據行指針的存放方式,避免了再次排序。咱們知道,排序消耗最大。如今表 t1 的直接拿出來就是按照主鍵 ID 排序。
mysql
   mysql> select * from t1;
   +-------+----------+--------+------+--------------+
   | id    | username | gender | age  | phone_number |
   +-------+----------+--------+------+--------------+
   | 10001 | 小花     | 女     |   18 | 18501877098  |
   | 10002 | 小王     | 男     |   20 | 17760500293  |
   | 10003 | 小趙     | 女     |   29 | 13581386000  |
   | 10004 | 小青     | 女     |   25 | 13456712000  |
   | 10005 | 小李     | 女     |   21 | 15827654555  |
   | 10006 | 小白     | 男     |   38 | 19929933000  |
   | 10007 | 小米     | 男     |   23 | 19800092354  |
   | 10008 | 小徐     | 女     |   22 | 18953209331  |
   | 10009 | 小何     | 男     |   35 | 19012378676  |
   +-------+----------+--------+------+--------------+
   9 rows in set (0.00 sec)
  1. 兩個葉子節點分別含有指向先後兩個節點的指針,這樣在插入新行或者進行頁分裂時,只須要移動對應的指針便可。

再來看下 INNODB 表的二級索引,以下圖所示:

image

INNODB 二級索引的非葉子節點保存索引的字段值,上圖索引爲表 t1 的字段 age。葉子節點含有索引字段值和對應的主鍵值。

這樣作的優勢是當出現數據行移動或者數據頁分裂時,避免二級索引沒必要要的維護工做。當數據須要更新的時候,二級索引不須要重建,只須要修改聚簇索引便可。

可是也有缺點:

  1. 二級索引因爲同時保存了主鍵值,體積會變大。特別是主鍵設計不合理的時候,好比用 UUID 作主鍵。下一篇我詳細介紹如何設計合理的主鍵。
  2. 對二級索引的檢索須要檢索兩次索引樹。第一次經過檢索二級索引葉子節點,找到過濾行對應的主鍵值;第二次經過這個主鍵的值去聚簇索引中查找對應的行。

舉個例子:

以下 SQL 語句,檢索年齡爲 23 的行記錄:

mysql
select * from t1 where age = 23;

會拆分紅如下兩個 SQL 語句:

先經過索引字段 age 找到對應的主鍵值:10005.

mysql
select id from t1 where age=23;

再去聚簇索引上根據主鍵 ID = 10005 檢索到須要的數據行,若是表第一次讀取,就須要回表。

mysql
select * from t1 where id = 10005;

不過 MySQL 對這塊作了很好的優化,提早作了數據預熱(數據預熱,這裏就不講了,能夠參考 MySQL 手冊,手冊上介紹的很詳細)。

本篇內容介紹到此,簡單回顧下本篇內容。本篇主要介紹 MySQL 常見的兩種引擎 MYISAM 和 INNODB 的索引組織方式以及各自的優缺點。有問題歡迎批評指正,下一篇我來介紹 MySQL 如何很好的對主鍵進行設計。


關於 MySQL 的技術內容,大家還有什麼想知道的嗎?趕忙留言告訴小編吧!

image

相關文章
相關標籤/搜索