讀書筆記: 數據庫與MySql(2)

索引在數據庫中非常重要,它決定着數據庫查詢的速度。


2 索引

索引是對數據庫表中的一列或多列的值進行排序的一種結構,使用索引可提高數據庫中特定數據的查詢速度。

2.1 索引簡介

索引是一個單獨的、存儲在磁盤上的數據庫結構,它們包含着對數據表裏所有記錄的引用指針。索引的優點主要有以下幾條:

  • 通過創建唯一索引,可以保證數據庫表中每一行數據的唯一性。
  • 索引可以大大提高數據的查詢速度。
  • 在實現數據的參考完整性方面,可以加速表與表之間的連接。
  • 在使用分組和排序子句進行數據查詢時,可以顯著減少查詢中分組和排序的時間。

當然增加索引也有許多不利的方面,主要如下:

  • 創建索引和維護索引需要耗費時間,並且隨着數據量的增加所耗費的時間也會增加。
  • 索引需要佔用磁盤空間,如果有大量的索引,索引文件可能比數據文件更快達到最大文件尺寸。
  • 對錶中的數據進行增刪改時,索引也要動態的維護,降低了數據的維護速度。

2.2 索引操作

MySql支持多種方法在單個或多個列上創建索引。在創建表時可以指定索引列,而如果表已經存在,則可以使用ALTER TABLE或者CERATE INDEX語句添加索引。

創建表時指定索引

CREATE TABLE `example`
(
…
PRIMARY KEY (`id`)
KEY `idx_name` (`name`),
KEY `idx_name_age` (`name`, `age`),
UNIQUE KEY `uk_order` (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

本例中,創建了主鍵索引,普通單列索引(前綴爲idx_),普通複合索引和唯一索引(前綴爲uk_)。

添加索引

ALTER TABLE tableName ADD [UNIQUE] INDEX `idx_xxx` (`xxx`[length], …) [ASC|DESC]

或者

CREATE [UNIQUE] INDEX `idx_xxx ON tableName (`xxx`[length], …) [ASC|DESC]

索引長度和索引排序順序是可選的。

刪除索引

ALTER TABLE tableName DROP INDEX index_name

DROP INDEX index_name ON tableName

2.3 索引的設計原則

索引設計不合理或缺少索引都會對數據庫性能造成障礙,高效的索引對於獲得良好的性能非常重要。設計索引時應該考慮以下原則:

(1)索引並非越多越好
索引不僅佔用磁盤空間,而且會影響INSERT, UPDATE, DELETE等語句的性能。因爲當表中的數據更改時,索引也會進行調整和更新。

(2)避免對經常更新的表建立過多的索引
對於經常用於查詢的字段應該創建索引,而對於經常更新的表則儘量減少索引的數量,避免建立無效索引。

(3)數據量少的表儘量不使用索引
數據量較少時直接全表遍歷查詢速度往往比通過索引更快。

(4)列值變化較小時不要建立索引
在列值變化較少時,如性別字段上只有“男”和“女”兩個不同值,建立索引不但不會提高查詢效率,反而會降低更新速度。

(5)當唯一性是某種數據本身的特徵時,指定唯一索引
使用唯一索引能確保定義的列的數據完整性,提高查詢速度。

(6)在頻繁進行排序或分組的列上建立索引

2.4 MySql索引

索引有很多種類型,可以爲不同的場景提供更好的性能。在MySql中,索引是在存儲引擎層而不是服務器層實現的。不同存儲引擎的索引工作方式和實現方式都是不一樣的。

MySql默認的索引類型是BTREE,更準確點來說是B+Tree。

MyISAM索引實現

MyISAM和InnoDB引擎均使用B+Tree索引,但實現方式不一樣。
MyISAM的B+Tree索引,葉節點的data域存放的是數據記錄的地址,如下圖(出自參考資料1)所示:
在這裏插入圖片描述
這裏假設表一共有三列,Col1爲主鍵,這是一個MyISAM表的主索引示意圖。如果還有輔助索引列如Col2,其索引結構是完全一樣的,只是將索引結構中的key換成對應的索引key:
在這裏插入圖片描述
在MyISAM引擎中,索引文件和數據文件是分開的。索引文件具有邏輯順序,而數據文件則不具有邏輯順序。查詢時先根據索引文件找到數據的物理地址,再繼續根據地址找到實際的數據記錄。這種索引方式也叫“非聚集索引”。

InnoDB索引實現

InnoDB也使用B+Tree作爲索引結果,但其實現方式跟MyISAM卻是不同的。
在InnoDB中,表數據文件本身就是一個索引文件,而且key就是數據表的主鍵。數據記錄存儲於葉子節點上。如下圖所示:
在這裏插入圖片描述
這種數據的物理存放順序與索引順序保持一致的索引也叫“聚集索引”。

如果沒有定義主鍵,InnoDB會選擇一個唯一的非空索引代替,如果沒有這樣的索引,InnoDB會隱式定義一個主鍵來作爲聚集索引。

另一個與MyISAM索引不同的是,InnoDB的輔助索引葉子節點數據域存放的是主鍵值而不是地址,如下圖所示:
在這裏插入圖片描述
使用輔助索引查詢時需要檢索兩次,先根據輔助索引獲得主鍵,然後用主鍵到數據文件(主索引文件)中檢索數據記錄。

在InnoDB引擎表中,不能使用過長的字段作爲主鍵,因爲所有的輔助索引都引用主索引,過長的主索引會使得輔助索引變得很大。另外,非單調字段也不合適作爲主鍵,因爲在插入新紀錄時爲了維持數據文件的B+Tree的特性,會頻繁的調整數據存儲位置,影響數據插入效率。

2.5 索引使用與優化

上文簡單提到過索引的設計原則。事實上,高效地選擇和使用索引有很多種方式,如何選擇並評估不同索引的性能影響,需要持續不斷地學習。

2.5.1 最左前綴原理與相關優化

根據BTree的結構特點可以推測出,BTree索引適用於全鍵值、鍵值範圍或鍵前綴查找。而在聯合索引中,鍵前綴也即是最左前綴。
具體點來說,BTree索引適用於以下情況:

(1) 全列匹配
全列匹配即是對索引列進行精確匹配。如果是聯合索引,則查詢條件中包含全部索引列。

(2) 範圍查找
範圍查找可以使用索引很好理解,因爲根據BTree的索引實現來看,只要找到範圍邊界值,在邊界值內的都是要找的數據。但要注意的是,範圍列必須是最左前綴(獨立索引可看作只有一個元素的聯合索引),範圍列後面的列無法用到索引。

(3) 最左前綴匹配
簡單點說,就是對於聯合索引idx_union(a,b,c),a,ab,abc均可以使用索引,其他的不行。

(4) 匹配列的前綴字符串
對於索引列col,使用where col like “xxx%”時可以使用索引。鍵前綴匹配,也可以理解爲特殊的最左前綴匹配。

(5) 精確匹配某一列並範圍匹配另一列
同樣可以理解爲特殊的最左前綴匹配。

當查詢條件不符合最左前綴時,無法使用索引。當查詢條件含有函數,存儲過程,條件表達式時,無法使用索引。當查詢條件中含有“OR,NOT IN, !=”等特殊符號時不能使用索引。

2.5.2 索引選擇性與前綴索引

索引選擇性是描述索引對數據記錄的區分度的,定義爲不重複的索引值和數據表的記錄總數(#T)的比值,範圍從1/#T到1之間。上文中索引的設計原則裏有一條說的是,當列值變化較小時不要建立索引,就是因爲此種情況下索引選擇性低,在檢索時不具有區分度。舉個栗子,假設性別列上有三種值,分別爲男,女和未知。就算建立了索引,那麼找到對應的索引鍵值之後仍然有大量記錄需要繼續檢索,體現不了索引的高檢索效率。

當數據庫中索引列的長度較長時,建立索引需要佔用很大的空間。對於BLOB、TEXT或者很長的VARCHAR類型的列,甚至不允許使用完整長度的索引。這種情況下可以使用前綴索引。

一般情況下,較長的列的前綴索引的選擇性也是足夠高的,基本可以滿足查詢性能。在使用前綴索引時,如何決定前綴的長度呢?可以通過計算索引選擇性來進行評估。舉個栗子來說,假設要在英文城市名列上建立前綴索引,則可以通過以下方式計算不同長度的前綴的索引選擇性:

SELECT COUNT(DISTINCT LEFT(`city`, 3))/COUNT(*) AS sel3, COUNT(DISTINCT LEFT(`city`, 4))/COUNT(*) AS sel4, COUNT(DISTINCT LEFT(`city`, 5))/COUNT(*) AS sel5 from example;

結果如下:
在這裏插入圖片描述
可以看到,當前綴索引長度達到7時,再增加索引長度,選擇性提升的幅度已經很小了。當然,這裏考慮的是平均選擇性(即假設數據分佈式均勻的),在某些情況下數據分佈不均勻,需要結合具體情況評估。

找到合適的前綴長度後,則可以創建索引如下:

ALTER TABLE example ADD KEY `idx_city` (city(7))

前綴索引是一種能使索引更小、更快的有效方法,但另一方面也有其缺點,MySql無法使用前綴索引做ORDER BY 和GROUP BY,也無法使用前綴索引做覆蓋掃描。

2.5.3 多列索引

經常有一種模糊的建議,即是將where條件裏面的列都建上索引。事實上這種做法是錯誤的。

雖然MySql5.0之後的版本中引入了“索引合併”的策略,一定程度上可以使用表上的多個單列索引來定位指定的行。但是,當MySql使用這種技術優化複雜查詢時,往往說明表上的索引建得不夠好。

如對於獨立的索引列a和b,現有查詢語句:SELECT * FROM example WHERE a = xxx AND b = xx,查詢時能夠同時使用這兩個單列索引進行掃描,並將結果合併。通過explain查看執行計劃,會在Extra列中看到合併索引。

具體點說,當出現服務器對多個索引做相交操作(多個AND條件)時,意味着需要一個包含所有列的多列索引,而不是多個獨立的單列索引;當服務器需要對多個索引做聯合操作(多個OR條件)時,通常需要耗費大量的CPU和內存資源在索引合併算法的緩存、排序和合並操作上,特別是當其中有些索引的選擇性不高的時候。優化器不會把這些計算到“查詢成本”中,這會使得查詢成本被低估,導致該執行計劃還不如全表掃描。

2.5.4 覆蓋索引

MySql也可以使用索引來直接獲取列的數據,這樣就不再需要讀取數據行。也就是說,當索引的葉子節點中已經包含要查詢的數據時,則沒有必要再回表查詢。這種索引中包含所有需要查詢字段的值的情況,稱之爲“覆蓋索引”。覆蓋索引只需要在索引中檢索一次即可。

參考資料:
1.https://blog.csdn.net/u013967628/article/details/84305511

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章