日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Mysql InnoDB索引分析

發布時間:2025/3/20 数据库 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mysql InnoDB索引分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

索引介紹

在 MySQL 中,索引是在存儲引擎層實現的,所以并沒有統一的索引標準,即不同存儲引擎的索引的工作方式并不一樣。而即使多個存儲引擎支持同一種類型的索引,其底層的實現也可能不同。InnoDB 存儲引擎在 MySQL 數據庫中使用最為廣泛,下面我們介紹一下InnoDB的索引模型。

在 InnoDB 中,表都是根據主鍵順序以索引的形式存放的,這種存儲方式的表稱為索引組織表。InnoDB 使用了 B+ 樹索引模型,所以數據都是存儲在 B+ 樹中的。每一個索引在 InnoDB 里面對應一棵 B+ 樹。

B+樹的介紹可以看以下文章:

Mysql的InnoDB索引原理詳解

索引維護

假設,我們有一個主鍵列為 ID 的表,表中有字段 k,并且在 k 上有索引。這個表的建表語句是:

mysql> create table T( id int primary key, k int not null, name varchar(16), index (k))engine=InnoDB;

表中 R1~R5 的 (ID,k) 值分別為 (100,1)、(200,2)、(300,3)、(500,5) 和 (600,6),兩棵樹的示例示意圖如下。

?

從圖中不難看出,根據葉子節點的內容,索引類型分為主鍵索引和非主鍵索引。

主鍵索引的葉子節點存的是整行數據。在 InnoDB 里,主鍵索引也被稱為聚簇索引(clustered index)。

非主鍵索引的葉子節點內容是主鍵的值。在 InnoDB 里,非主鍵索引也被稱為二級索引(secondary index)。

B+ 樹為了維護索引有序性,在插入新值的時候需要做必要的維護。以上面這個圖為例,如果插入新的行 ID 值為 700,則只需要在 R5 的記錄后面插入一個新記錄。如果新插入的 ID 值為 400,就相對麻煩了,需要邏輯上挪動后面的數據,空出位置。

而更糟的情況是,如果 R5 所在的數據頁已經滿了,根據 B+ 樹的算法,這時候需要申請一個新的數據頁,然后挪動部分數據過去。這個過程稱為頁分裂。在這種情況下,性能自然會受影響。

除了性能外,頁分裂操作還影響數據頁的利用率。原本放在一個頁的數據,現在分到兩個頁中,整體空間利用率降低大約 50%。

基于上面的索引維護過程說明,我們來討論一個案例:

你可能在一些建表規范里面見到過類似的描述,要求建表語句里一定要有自增主鍵。當然事無絕對,我們來分析一下哪些場景下應該使用自增主鍵,而哪些場景下不應該。

自增主鍵是指自增列上定義的主鍵,在建表語句中一般是這么定義的: NOT NULL PRIMARY KEY AUTO_INCREMENT。插入新記錄的時候可以不指定 ID 的值,系統會獲取當前 ID 最大值加 1 作為下一條記錄的 ID 值。

也就是說,自增主鍵的插入數據模式,正符合了我們前面提到的遞增插入的場景。每次插入一條新記錄,都是追加操作,都不涉及到挪動其他記錄,也不會觸發葉子節點的分裂。

除了考慮性能外,我們還可以從存儲空間的角度來看。假設你的表中確實有一個唯一字段,比如字符串類型的身份證號,那應該用身份證號做主鍵,還是用自增字段做主鍵呢?

由于每個非主鍵索引的葉子節點上都是主鍵的值。如果用身份證號做主鍵,那么每個二級索引的葉子節點占用約 20 個字節,而如果用整型做主鍵,則只要 4 個字節,如果是長整型(bigint)則是 8 個字節。

顯然,主鍵長度越小,普通索引的葉子節點就越小,普通索引占用的空間也就越小。

所以,從性能和存儲空間方面考量,自增主鍵往往是更合理的選擇。

有沒有什么場景適合用業務字段直接做主鍵的呢?還是有的。比如,有些業務的場景需求是這樣的:

  • 只有一個索引;

  • 該索引必須是唯一索引。

  • 由于沒有其他索引,所以也就不用考慮其他索引的葉子節點大小的問題。這時候我們就要優先考慮上一段提到的“盡量使用主鍵查詢”原則,直接將這個索引設置為主鍵,可以避免每次查詢需要搜索兩棵樹。

    索引覆蓋

    還是上面這張表,如果我執行select * from T where k between 3 and 5,需要執行幾次樹的搜索操作,會掃描多少行?

    現在,我們一起來看看這條 SQL 查詢語句的執行流程:

  • 在 k 索引樹上找到 k=3 的記錄,取得 ID = 300;

  • 再到 ID 索引樹查到 ID=300 對應的 R3;

  • 在 k 索引樹取下一個值 k=5,取得 ID=500;

  • 再回到 ID 索引樹查到 ID=500 對應的 R4;

  • 在 k 索引樹取下一個值 k=6,不滿足條件,循環結束。

  • 這里我們查找的是3-5之間的數,為什么還要查找6呢,因為進行索引搜索的時候,會對數據挨個進行搜索,直到搜索到不符合要求的數據。

    在這個過程中,回到主鍵索引樹搜索的過程,我們稱為回表。可以看到,這個查詢過程讀了 k 索引樹的 3 條記錄(步驟 1、3 和 5),回表了兩次(步驟 2 和 4)。

    那么如何避免回表呢?

    如果執行的語句是 select ID from T where k between 3 and 5,這時只需要查 ID 的值,而 ID 的值已經在 k 索引樹上了,因此可以直接提供查詢結果,不需要回表。也就是說,在這個查詢里面,索引 k 已經“覆蓋了”我們的查詢需求,我們稱為覆蓋索引。

    由于覆蓋索引可以減少樹的搜索次數,顯著提升查詢性能,所以使用覆蓋索引是一個常用的性能優化手段。

    需要注意的是,在引擎內部使用覆蓋索引在索引 k 上其實讀了三個記錄,R3~R5(對應的索引 k 上的記錄項),但是對于 MySQL 的 Server 層來說,它就是找引擎拿到了兩條記錄,因此 MySQL 認為掃描行數是 2。

    下面是這兩條語句的分析:

    1)EXPLAIN select * from T where k between 3 and 5

    ?

    能夠命中索引,但是name字段必須通過回表查詢得到>>>Using index condition

    2)EXPLAIN select id from T where k between 3 and 5

    ?

    能夠命中索引,且索引中本來就存儲的id,所以不需要回表>>>Using index

    總結

    以上是生活随笔為你收集整理的Mysql InnoDB索引分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。