阿里面試官:什么是MySQL索引,為什么要有索引?


一、什么是索引?

索引就好比字典的目錄一樣
我們通常都會先去目錄查找關鍵偏旁或者字母再去查找
要比直接翻查字典查詢要快很多

二、為什么要有索引?

然而我們在使用mysql數據庫的時候也像字典一樣有索引的情況下去查詢,肯定速度要快很多

2.1問題:

1.mysql數據存儲在什么地方?

磁盤

2.查詢數據慢,一般卡在哪?

IO

3.去磁盤讀取數據,是用多少讀取多少嗎?

磁盤預讀

局部性原理:數據和程序都有聚集成群的傾向,同時之前被訪問過的數據很可能再次被查詢,空間局部性,時間局部性

磁盤預讀:內存和磁盤發生數據交互的時候,一般情況下有一個最小的邏輯單元,頁。 頁一般由操作系統覺得大小,4k或8k,而我們在進行數據交互的時候,可以取頁的整數倍來讀取。關注公眾號:程序員追風,回復 012 即可獲取一份578頁PDF文檔的MySQL學習筆記

innodb存儲引擎每次讀取數據,讀取16k

4.索引存儲在哪?

磁盤,查詢數據的時候會優先將索引加載到內存中

5.索引在存儲的時候,需要什么信息?需要存儲存儲什么字段值?

key:實際數據行中存儲的值

文件地址

offset:偏移量

6.這種格式的數據要使用什么樣的數據結構來進行存儲?

key-values

哈希表,樹(二叉樹、紅黑樹、AVL樹、B樹、B+樹)

7.mysql索引系統中不是按照剛剛說的格式存儲的,為什么?

OLAP:聯機分析處理----對海量歷史數據進行分析,產生決策性的策略----數據倉庫—Hive

OLTP:聯機事務處理----要求很短時效內返回對應的結果----數據庫—關系型數據庫(mysql、oracle)

三、mysql的索引數據結構

3.1哈希表:

HashMap數組加鏈表的結構,不適合作為索引的原因:

1.哈希沖突會造成數據散列不均勻,會產生大量的線性查詢,比較浪費時間

2.不支持范圍查詢,當進行范圍查詢的時候,必須挨個遍歷

3.對於內存空間的要求比較高


優點: 如果是等值查詢,非常快


在mysql中有沒有hash索引?

1.memory存儲引擎使用的是hash索引

2.innodb支持自適應hash

create table test(id int primary key,name varchar(30))
engine='innodb/memory/myisam'
-- 5.1之后默認innodb

3.2樹:

樹這種數據結構有很多,我們常見的有:
二叉樹、BST、AVL、紅黑樹、B樹、B+樹

①二叉樹:無序插入
在這里插入圖片描述

這就是我們的樹的結構圖,但是二叉樹的數據插入是無序的,也就是說當需要查找的時候,還是得一個一個挨着去遍歷查找

②BST(二叉搜索樹):
插入的數據有序,左子樹必須小於根節點,右子樹必須大於根節點--------使用二分查找來提高效率
在這里插入圖片描述

這樣的話如果要查詢數據,可以通過二分查找,快速縮小范圍,減少了時間復雜度
但是如果插入的順序是升序或者降序的話,樹的形狀會變成如下:
在這里插入圖片描述

此時二叉搜索樹就會退化成鏈表,時間復雜度又會變成O(n)

③AVL:平衡二叉樹
為了解決上述問題,通過左旋轉或右旋轉讓樹平衡
最短子樹跟最長子樹高度只差不能超過1
在這里插入圖片描述

由圖我們可以看到,當順序插入的時候,會自動的進行旋轉,以達到平衡
但是會通過插入性能的損失來彌補查詢性能的提升
當我們插入的數據很多時候,而查詢很少的時候,由於插入數據會旋轉同樣會消耗很多時間
④紅黑樹(解決了讀寫請求一樣多)
同樣是經過左右旋讓樹平衡起來,還要變色的行為
最長子樹只要不超過最短子樹的兩倍即可
在這里插入圖片描述

查詢性能和插入性能近似取得平衡
但是隨着數據的插入、發現樹的深度會變深,樹的深度會越來越深,意味着IO次數越多,影響數據讀取的效率

⑤ B樹
為了解決上述數據插入過多,樹深度變深的問題,我們采用B樹
把原來的有序二叉樹變成有序多叉樹
在這里插入圖片描述

舉例: 如果要查詢select * from table where id=14?

  1. 第一步,將磁盤一加載到內存中,發現14<16,尋找地址磁盤2
  2. 第二步,將磁盤二加載到內存中,發現14>11,尋找地址磁盤7
  3. 第三步,將磁盤七加載到內存中,發現14=14,讀取data,取出data,結束
    思考:B樹就是完美的嘛?
    問題1: B樹不支持范圍查詢的快速查找,如果我們查詢一個范圍的數據,查找到范圍一個邊界時,需要回到根節點重新遍歷查找,需要從根節點進行多次遍歷,即便找到范圍的另一個邊界,查詢效率會降低。
    問題2: 如果data存儲的是行記錄,行的大小隨着列數的增多,所占空間會變大。這時,一個頁中可存儲的數據量就會變少,樹相應就會變高,磁盤IO次數就會變大。
    思考2:三層B樹能夠存儲多少條記錄?
    答: 假設一個data為1k,innodb存儲引擎一次讀取數據為16k,三層即161616=4096;
    但是往往在開發中,一個表的數據要遠遠大於4096,難道要繼續加層,這樣豈不就加大了IO

四、為什么使用B+樹?

實際存儲表數據的時候,怎么存儲呢?
key
完整的數據行
改造B+樹

B+樹對B樹進行了改進,把數據全放在了葉子節點中,葉子節點之間使用雙向指針連接,最底層的葉子節點形成了一個雙向有序鏈表。
例如: 查詢范圍 select * from table where id between 11 and 35?

  1. 第一步,將磁盤一加載到內存中,發現11<28,尋找地址磁盤2
  2. 第二步,將磁盤二加載到內存中,發現10>11>17,尋找地址磁盤5
  3. 第三步,將磁盤五加載到內存中,發現11=11,讀取data
  4. 第四步,繼續向右查詢,讀取磁盤5,發現35=35,讀取11-35之間數據,結束
    由此可見,這樣的范圍查詢比B樹速度提高了不少

對比B樹和B+樹?

  • 葉子節點中才放數據

  • 非葉子節點中不存儲數據

  • B+樹每個節點包含更多個節點,這樣做的好處,可以降低樹的高度,同時將數據范圍變成多個區間,區間越多查詢越快

問題: 創建索引時用int還是varchar?

答:視情況而定,但是記住一定讓key越小越好

五、索引的創建

在創建索引之前,我先說一下存儲引擎
存儲引擎: 表示不同的數據在磁盤的不同表現形式
大家去觀察mysql的磁盤文件會發現
innodb: innodb的數據和索引都存儲在一個文件下.idb
myisam: myisam的索引存儲在.MYI文件中,數據存儲在.MYD中

5.1聚簇索引和非聚簇索引

概念:判斷是否是聚簇索引就看數據和索引是否在一個文件中
innodb:

  1. 只能有一個聚簇索引,但是有很多非聚簇索引
  2. 向innodb插入數據的時候,必須要包含一個索引的key值
  3. 這個索引的key值,可以是主鍵,如果沒有主鍵,那么就是唯一鍵,如果沒有唯一鍵,那么就是一個自生成的6字節的rowid

myisam: 非聚簇索引

MySQL—innodb----B+樹
索引和數據存儲在一起,找到索引即可讀取對應的數據
在這里插入圖片描述

MySQL—myisam----B+樹
索引和存儲數據的地址在一起,找到索引得到地址值,再通過地址找到對應的數據
在這里插入圖片描述

5.2回表

接下來,我會創建一張案例表給大家展示

CREATE TABLE user_test(
id INT PRIMARY KEY AUTO_INCREMENT,-- id為主鍵
uname VARCHAR(20) ,
age INT,
gender VARCHAR(10),
 KEY `idx_uname` (`uname`) -- 索引選擇為名字
)ENGINE = INNODB;

INSERT INTO user_test VALUES(1,'張三',18,'男');
INSERT INTO user_test VALUES(NULL,'馬冬梅',19,'女');
INSERT INTO user_test VALUES(NULL,'趙四',18,'男');
INSERT INTO user_test VALUES(NULL,'王老七',22,'男');
INSERT INTO user_test VALUES(NULL,'劉燕',16,'女');
INSERT INTO user_test VALUES(NULL,'萬寶',26,'男');

select * from user_test where uname = '張三';
-- 當我們表中有主鍵索引的時候,我們再去設置一個uname為索引,那么此時這條sql語句的查詢過程應該如下:

首先先根據uname查詢到id,再根據id查詢到行的信息
這樣的操作走了兩棵B+樹,就是回表
當根據普通索引查詢到聚簇索引的key值之后,再根據key值在聚簇索引中獲取數據
我們可以發現這樣的操作是很浪費時間的,因此我們日常操作的時候,盡量減少回表的次數

5.3覆蓋索引

select id,uname from table where uname = '張三';
-- 根據uname 可以直接查詢到id,uname兩個列的值,直接返回即可
-- 不需要從聚簇索引查詢任何數據,此時叫做索引覆蓋

5.4最左匹配

在說最左匹配之前,我們先聊一下幾個名詞
主鍵(一般為一個列)-------->聯合主鍵(多個列)
索引-------->聯合索引(可能包含多個索引列)

-- 假設有一張表,有id,name,age,gender四個字段,id是主鍵,name,age是組合索引列
-- 組合索引使用的時候必須先匹配name,然后匹配age

select * from table where name = ? and age = ? ;-- 生效
select * from table where name = ?;-- 生效
select * from table where age = ? ;-- 不生效
select * from table where age = ? and name = ? ;-- 生效

--在mysql內部有優化器會調整對應的順序

5.5索引下推

mysql5.7之后,默認支持的一個特點
舉一個例子:

select * from table where name = ? and age = ? ;
-- mysql里的三層架構:
-- 客戶端:JDBC
-- 服務端:server
-- 存儲引擎:數據存儲
在沒有索引下推之前,根據name從存儲引擎中獲取符合規則的數據,在server層對age進行過濾
有索引下推之后,根據name、age兩個條件從存儲引擎中獲取對應的數據

分析:有索引下推的好處,如果我們有50條數據,我們通過過濾會得到10條數據,如果沒有索引下推,會先獲取50條再去排除得到10條,而有了下推之后,我們會直接在存儲引擎就過濾成了10條


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM