MySql學習(三)索引

索引是什麼

官方介紹索引是幫助MySQL高效獲取數據的數據結構。更通俗的說,數據庫索引好比是一本書前面的目
錄,能加快數據庫的查詢速度。
索引的優勢和劣勢

優勢

可以提高數據檢索的效率,降低數據庫的IO成本,類似於書的目錄。 – 檢索
通過索引列對數據進行排序,降低數據排序的成本,降低了CPU的消耗。 --排序
被索引的列會自動進行排序,包括【單列索引】和【組合索引】,只是組合索引的排序要複雜一
些。(可以參考後續講的B+TREE的結構,幫助理解)
如果按照索引列的順序進行排序,對應order by語句來說,效率就會提高很多。
如果where條件列符合索引的最左前綴原則的話,在存儲引擎層會有一層index filter處理。(索引下推 ICP)
如果檢索的字段都包含在索引當中可以減少回表查詢從而提高效率(索引覆蓋

劣勢

索引會佔據磁盤空間。
索引雖然會提高查詢效率,但是會降低更新表的效率。比如每次對錶進行增刪改操作,MySQL不僅要保存數據,還有保存或者更新對應的索引文件。

索引原理分析

索引的存儲結構

  1. 索引是在存儲引擎中實現的,也就是說不同的存儲引擎,索引會使用不同的數據結構
  2. MyISAM和InnoDB存儲引擎:只支持B+ TREE索引, 也就是說默認使用B+TREE,不能夠更換
  3. MEMORY/HEAP存儲引擎:支持HASH和BTREE索引

B-TREE和B+TREE

在這裏插入圖片描述

  1. B樹是爲了磁盤或其它存儲設備而設計的一種多叉平衡查找樹。
  2. B樹的高度一般都是在2-4這個高度,樹的高度直接影響IO讀寫的次數。
  3. 如果是三層樹結構—支撐的數據可以達到20G,如果是四層樹結構—支撐的數據可以達到幾十T B和B+的區別
  4. B樹和B+樹的最大區別在於非葉子節點是否存儲數據的問題。
  5. B樹是非葉子節點和葉子節點都會存儲數據。
  6. B+樹只有葉子節點纔會存儲數據,而且存儲的數據都是在一行上,而且這些數據都是有指針指向
    的,也就是有順序的

注:所以上文的留下的問題,因爲B+樹葉子節點是有順序的且類似於鏈表的結構,所以會大大的縮短排序的時間,而且是隻有包含在索引中的列的排序才能享受到這個待遇。

非聚集索引(MyISAM)

MyISAM索引(.myi)和數據(,myd)是分開存放的,MyISAM的B+樹種葉子節點存放的是數據在數據文件(.myd)的指針位置

在物理結構篇中講述了MyISAM中的存儲結構和上述結構也有一個對照與論證

.frm文件:主要存放與表相關的數據信息,主要包括表結構的定義信息
.myd文件:主要用來存儲表數據信息。
.myi文件:主要用來存儲表數據文件中任何索引的數據樹。

在這裏插入圖片描述
其中test表使用的是MyISAM引擎,結構如上圖所示。

主鍵索引

![在這裏插入圖片描述](https://img-blog.csdnimg.cn/2020030118593943.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2JhaWR1XzI5NjA5OTYx,size_16,color_FFFFFF,t_70

輔助索引(非主鍵索引)

MyISAM的輔助索引與主鍵索引是相同的。存放的都是索引值與數據地址的鍵值對。

聚集索引(InnoDB)

主鍵索引

在這裏插入圖片描述

輔助索引

在這裏插入圖片描述
InnoDB索引數據是存放在一起的,InnoDB的B+樹種葉子節點中主鍵索引存放的是完整的數據,而輔助索引中存放的是索引值與主鍵值。

在物理結構篇中講述了InnoDB中的存儲結構和上述結構也有一個對照與論證

1 .frm文件:主要存放與表相關的數據信息,主要包括表結構的定義信息.
2. .ibd:使用獨享表空間存儲表數據和索引信息,一張表對應一個ibd文件。

在這裏插入圖片描述
其中user表使用的是InnoDB引擎,結構如上圖所示。

一些名詞解釋

回表

結合InnoDB的索引數據結構,由於非主鍵查詢,第一次僅能得到索引的值以及對應主鍵的值,需要回到主鍵的索引樹種查詢對應的行記錄,這種行爲就叫做回表

索引覆蓋

索引覆蓋是針對回表來說的,檢索所需的項目都包含在索引中,那麼就不需要回表查詢,這樣的效率相對於回表查詢要高。是否用到了索引覆蓋可以根據查詢計劃中 extra的值來判斷。
根據上面的InnoDB索引文件結構,索引中存放的是主鍵值,或者輔助索引的值,如果你查詢只需要鍵值,那麼直接就可以得到就不需要再根據索引中存放的主鍵再去檢索主鍵索引了。
舉個例子:

CREATE TABLE `tuser`  (
  `id` int(11) NOT NULL,
  `NAME` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `age` int(11) NULL DEFAULT NULL,
  `sex` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `address` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_name_age`(`NAME`, `age`) USING BTREE,
  INDEX `idx_sex`(`sex`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

表結構如上述所示
執行查詢,結果如下(不同數據庫版本可能有所不同,我用的版本是5.7.24
在這裏插入圖片描述
這裏就是用到了索引覆蓋,如果我把select項目改成*,或者不符合最做前綴原則那麼,就需要回表進行查詢了
在這裏插入圖片描述
在這裏插入圖片描述
重點: 索引覆蓋是針對一個索引來說的,比如tuser表,單獨使用主鍵索引,或者輔助索引用到了索引覆蓋都是可以的,但是要根據主鍵值查詢name則不行。
在這裏插入圖片描述
在這裏插入圖片描述
可以看到這兩個查詢都用到了所以覆蓋,一個是索引idx_name_age的索引覆蓋,一個是主鍵的索引覆蓋。
但是我如果根據主鍵值查詢name則無法進行索引覆蓋。
在這裏插入圖片描述
再注意: 索引覆蓋不要理解有誤區,只要是能從當前索引樹上取得所有的信息不需要再去查詢主鍵索引樹就是索引覆蓋。就如下圖所示,idx_name_age樹中節點中存放的鍵值對爲(name+age : id)所以檢索id即使不符合idx_name_age的最左前綴原則,也是屬於索引覆蓋的範疇。
在這裏插入圖片描述

索引下推

在MySql5.6之前檢索分爲兩個步驟

  1. 根據最左前綴原則在存儲引擎層確定index key的檢索範圍,然後將回表讀取這些數據
  2. 返回給SQL SERVER層再根據剩下的where條件進行過濾

而在MySql5.7之後,把第二步一部分操作下推到存儲引擎層,即可以根據索引上的值過濾一些不符合條件的數據(稱爲 index filter),再返回到SQL SERVER層進行過濾(稱爲 table filter)過程如下

  1. 根據最左前綴原則在存儲引擎層確定index key的檢索範圍
  2. 根據索引過濾掉一些數據,再回表查詢讀取
  3. 返回給SQL SERVER層再根據剩下的where條件進行過濾
    這一改動就稱之爲索引下推

直觀體驗就是執行計劃中 extra 提示Using index condition
在這裏插入圖片描述
腦筋急轉彎(大霧)
如果把上面的*改成name,age進行索引覆蓋還會出現索引下推麼?

答案是:
不會出現,看原理我們得出索引下推是出現回表查詢的時候纔會用到的技術,那麼索引覆蓋不會回表,那麼就不會出現索引下推。

在這裏插入圖片描述

索引下推的好處

直接在存儲引擎層過濾,省去了這些記錄回表查詢以及返回到SQL SERVER曾的開銷。

索引的選擇

索引優先選擇包含所有信息的列,如果有多個索引都包含全部信息,存儲引擎自動選擇鍵值最短的索引,這個跟存儲引擎的工作機制有關,存儲引擎一下子讀取多條數據的鍵值進行比對,每次讀取到內存中的大小是一定的,那麼鍵值越短,每次讀取的條數越多,IO越少,效率越高。

CREATE TABLE `tuser` (
  `id` int(11) NOT NULL,
  `NAME` varchar(100) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` char(1) DEFAULT NULL,
  `address` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name_age` (`NAME`,`age`),
  KEY `idx_sex` (`sex`),
  KEY `idx_age_address` (`age`,`address`),
  KEY `idx_sex_address` (`sex`,`address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

如果我要單一查找address,該信息在idx_age_address和idx_sex_address中都是存在的且都不需要回表查詢,而idx_age_address鍵長爲308,idx_sex_address爲307所以會選擇idx_sex_address
在這裏插入圖片描述

索引失效

講索引失效之前,你要牢記索引的存儲結構,就是

  1. 主鍵索引是主鍵值與整個行記錄信息。
  2. 輔助索引存放的是輔助索引所包含的列的信息與主鍵值。

結合上邊的例子,我們插入幾條數據

INSERT INTO `tuser` VALUES (1, 'zhangsan', 20, '0', '致真 大廈');
INSERT INTO `tuser` VALUES (2, 'lisi', 23, '0', '致真 大廈');
INSERT INTO `tuser` VALUES (3, 'wangwu', 22, '0', '致真 大廈');
INSERT INTO `tuser` VALUES (4, 'zhuliu', 21, '0', '致真 大廈');

那麼索引PRIMARY(主鍵索引)中存放的結構大體如下:
在這裏插入圖片描述
索引idx_name_age中存放的結構大體如下:
在這裏插入圖片描述
(一定都是排過序的)

不符合最左前綴原則

其實根據鍵值很好理解比如,我想查詢name=lisi的值,雖然索引idx_name_age鍵值是name和age拼接起來的,但是我依然可以根據鍵值是否以lisi開頭來快速過濾出來。相反的我只檢索age=20即使idx_name_age中裏面有age字段但是在鍵值中間所以就無法使用該索引快速過濾只能全文檢索了
在這裏插入圖片描述

在這裏插入圖片描述

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