注意:下面討論的都是MySQL5.6版本中的innodb引擎。
比較規範的數據庫表設計(包括我們公司)都會有一條不成文的規定,那就是給每張表一個自增主鍵。那麼自增主鍵除了有數據的唯一性外,還有什麼所用呢?爲什麼要有自增主鍵?
之前我轉發的《58到家數據庫30條軍規解讀》中解釋道:
- 主鍵遞增,數據行寫入可以提高插入性能,可以避免page分裂,減少表碎片提升空間和內存的使用
- 主鍵要選擇較短的數據類型, Innodb引擎普通索引都會保存主鍵的值,較短的數據類型可以有效的減少索引的磁盤空間,提高索引的緩存效率
- 無主鍵的表刪除,在row模式的主從架構,會導致備庫夯住
第三條先不必關注,我們來看看前兩條。
爲什麼能提高插入性能呢,避免page分頁又是怎麼回事?
這裏就不得不說一下聚集索引了。
聚集索引(Clustered Index)
一個聚集索引定義了表中數據的物理存儲順序。如何理解聚集索引呢,好比一個電話本,比如一個電話本是按照姓氏排序,並且電話號碼緊跟着後面。因爲聚集索引決定了表中數據的物理存儲順序,那麼一個表則有且只有一個聚集索引。一個聚集索引可以包含多個列。好比一個電話本是基於名字,姓氏同時排序。
Innodb的聚集索引
Innodb的存儲索引是基於B+tree,理所當然,聚集索引也是基於B+tree。與非聚集索引的區別則是,聚集索引既存儲了索引,也存儲了行值。當一個表有一個聚集索引,它的數據是存儲在索引的葉子頁(leaf pages)。因此innodb也能理解爲基於索引的表。
* 那麼Innodb如何決定那個索引作爲聚集索引呢?*
Innodb如何選擇一個聚集索引
對於Innodb,主鍵毫無疑問是一個聚集索引。但是當一個表沒有主鍵,或者沒有一個索引,Innodb會如何處理呢。請看如下規則
如果一個主鍵被定義了,那麼這個主鍵就是作爲聚集索引
如果沒有主鍵被定義,那麼該表的第一個唯一非空索引被作爲聚集索引
如果沒有主鍵也沒有合適的唯一索引,那麼innodb內部會生成一個隱藏的主鍵作爲聚集索引,這個隱藏的主鍵是一個6個字節的列,改列的值會隨着數據的插入自增。
還有一個需要注意的是:
次級索引的葉子節點並不存儲行數據的物理地址。而是存儲的該行的主鍵值。
所以:一次級索引包含了兩次查找。一次是查找次級索引自身。然後查找主鍵(聚集索引)
現在應該明白了吧,建立自增主鍵的原因是:
Innodb中的每張表都會有一個聚集索引,而聚集索引又是以物理磁盤順序來存儲的,自增主鍵會把數據自動向後插入,避免了插入過程中的聚集索引排序問題。聚集索引的排序,必然會帶來大範圍的數據的物理移動,這裏面帶來的磁盤IO性能損耗是非常大的。
而如果聚集索引上的值可以改動的話,那麼也會觸發物理磁盤上的移動,於是就可能出現page分裂,表碎片橫生。
解讀中的第二點相信看了上面關於聚集索引的解釋後就很清楚了。
雖然遵循上面的原則也沒錯,但某些特殊的情況也是可以自己指定一些非自增主鍵爲聚集索引的。如:
- 當數據量大,但長時間不會被更新的;
- 新生成的數據的索引本來就是按照自增的順序增加的等等。
舉個栗子,(只是栗子啊,現實中不太可能):
有一家公司裏的員工上百萬,有關員工的個人信息幾年都不會發生變化,那麼以員工的user_id號來作爲各個表的索引就很適合。因爲一個員工的信息都按照物理順序呢排列在一起了,避免了磁盤移動查找數據的IO時間。比如說員工屬於幾個部門,一下子就查到了,不同分不同的地址去挨個進行磁盤掃描。
栗子從簡,哈哈哈哈,就這樣子吧,我也不太會寫,簡單的記錄下來了~