初識MySQL · 索引

目錄

前言:

重溫磁盤

認識索引

為什么這么做,怎么做

重談page

聚簇索引VS非聚簇索引

回表查詢

?索引分類


前言:

前文我們主要是介紹了MySQL的一些基本操作,增刪查改一類的操作都介紹了,并且因為大多數情況下,查詢的頻率遠遠高于其他操作,所以我們在查詢上面,也是花費了一番功夫的。

那么本文,我們來介紹查詢背后的機制——索引,大多數同學第一次接觸到索引的時候,第一感覺肯定是:索引?我們C語言平常學習的索引嗎?類似于數組下標一類的? 實則不然,在MySQL的索引大有不同。

我們在平時構建算法的時候,影響效率的不止有算法本身,實際上還有對數據的組織方式,比如同樣是增刪查改,對于順序表和堆來說,二者對于這四個操作的效率肯定大不相同。那么我們在優化算法的時候,就可以著手于對數據的組織方式進行優化。索引的存在就是優化數據的組織方式的

所以,索引既然是優化的數據的組織方式的,那么,索引不就是數據結構嗎?


重溫磁盤

對于MySQL來說,我們有著和Linux中“萬物皆文件”的一樣的概念,即“一切皆表”,也就是說我們認為MySQL的所有數據都是一張一張的表構成的,不管是我們創建的,還是查詢等操作產生的中間表,我們認為都是表。

即便我們從文件的角度來看,每次當我們創建了表之后,我們在MySQL的目錄也能看到對應的文件生成:

類似于這個,目前我的MySQL版本是8.0的,如果有的同學的MySQL版本是5.7,那么還會看到生成了對應的表結構定義文件.frm,但是不幸的是.frm文件在8.0之后已經被棄用了,原因大致如下:

問題解釋
一致性差.frm 是單文件,容易與 .ibd 不一致
不支持事務表結構變更不具備事務能力
不支持原子性DDL 操作不可回滾
無法跨平臺.frm 二進制結構與平臺、版本強耦合
不利于統一管理數據、索引、結構分散在多個文件中

好了,現在我們有一個認識:MySQL中不管是創建數據庫,還是創建表,都在會實際的文件系統中創建真實的目錄和文件

那么不就意味著,當我們在MySQL創建表的時候,是要和磁盤等外設打交道的,既然是要和磁盤等外設打交道,那么就要考慮IO的問題了,但是我們同時都知道的,根據馮諾依曼結構體系,處于應用層的MySQL是不能直接和硬盤打交道的,能和硬盤等外設打交道的只有OS,所以問題從MySQL如何和磁盤打交道轉變為了MySQL如果通過OS,間接的和磁盤打交道

那么我們這個小標題,要解決的問題是OS如何和磁盤打交道的

基本的磁盤長什么樣我們都知道,由多個盤片摞在一起,并且每個面都有一個磁頭,一個盤片我們可以分出磁道,扇區等單位出來,磁道是一個一個的同心圓,多個磁道構成一個盤面,每個磁道被劃分為一個一個的區域段,這個區域段我們就稱為扇區。

那么有了磁頭Heads,柱面Cylinder(等價于磁道),扇區(Sector),我們就能精準的找到磁盤中的某一個區域,這種磁盤數據定位方式叫做CHS,但是我們要知道,這是早期做法,因為它的數量有嚴格的限制,最多8.4GB左右,所以現在OS早已通過LBA,即Logical Block Addressing 這種邏輯尋址方式進行尋址。不過在早期的時候,磁盤控制器要求只能通過CHS進行訪問,所以早期的時候是有LBA向CHS進行轉換的,不過在當今的時代,基本上已經拋棄了CHS的做法。

但是我們清楚的時候,在第一次介紹文件系統的時候,我們清楚的知道OS和硬盤的數據交互一次性是4KB,也就是說如果我們只是修改一個字節,OS也要在磁盤中給你找到這1字節周圍的4KB數據,然后再修改,這實際上用到了局部性原理,為了提高效率的。

現在我們就清楚了OS和磁盤打交道的基本單位是4KB,那么疑問來了,MySQL通過OS和磁盤打交道的基本單位是多少呢?

答案是:16KB。也就是說MySQL作為應用層服務來說,要交互一次數據,至少都是16KB起步,那么當交互的時候,OS怎么搬來的16KB數據MySQL不管,它只管要就可以了。那么我們如何驗證MySQL交互數據的基本單位是16KB呢?我們可以通過命令show global status like 'innode_page_size'進行查看:

可以看到確實是16KB。

所以我們可以將MySQL和磁盤交互數據的時候應該是這樣的:

其中的buffer pool是MySQL自己申請的緩沖區,相當于TCP中read也有自己的緩沖區一樣。

那么我們從這里得出結論:MySQL通過OS和磁盤交互的基本數據單位是16KB


認識索引

前文我們花費些許功夫,通過磁盤的CHS,引出了操作系統的LBA,并且結合了OS與磁盤的基本交互是4KB,引出了MySQL和磁盤的交互單位是16KB,但是實際上,事實遠遠沒有這么簡單。那么16KB為什么不簡單我們后面介紹。

對于索引,是通過改變數據的組織方式然后提高MySQL的效率的,所以它的本質,其實就是數據結構,那么我們來理解MySQL的索引是如何工作的。

首先我們建立一張表,這里我們最好加上主鍵約束

注意我們不是按照id的大小插入數據的,我們是隨機插入的,但是當我們一查看數據的時候,我們發現:

它默認為我們進行了排序。

那么從這個現象,我們引出兩個問題這個操作是誰做的,為什么要這么做? 重談16KB

為什么這么做,怎么做

先給結論:MySQL自己做的,這么做的目的是為了方便引出目錄

對于數據庫來說,它的基本數據單位16KB都是要管理起來的,在源碼中,它的基本管理方式是使用鏈表,就像這樣:

那么現在,我們清楚每個16KB里面有數據,還有前后指針,并且在應用層是通過雙向鏈表管理起來的,還發現每個16KB,被稱呼為page。

對于每個page來說,它里面有大量的數據,不同的page鏈接在一起,但是這樣的數據組織方式是雙向鏈表,數據查找的時候,仍然是線性的,可以說在大量數據的時候,這種模式下MySQL會一個一個遍歷的每個page,按照順序遍歷,找到了直接返回即可。

這不就是一個一個的查嘛?那么時間復雜度就是O(N),這個N在實際的數據中,可能是幾百萬,可能是幾億,那你讓如此脆弱的MySQL完成這么個操作,那它一天啥事也不用干了,光崩潰就可以了。

所以這里,我們引入了目錄

首先在每個page里面,引入一個目錄:比如一號目錄映射到了1,二號目錄映射到了3,那么我要查2號數據的時候,通過目錄1,找到了1號數據,然后往下走一步,就是2號數據了。這個過程的思想就是通過目錄,一次性干掉大部分數據。通過目錄,能一次性篩選掉其他目錄的所有的指向數據,這個操作實際上就是我們平時讀書的時候,查閱目錄的方式是一樣的。

但是這種操作實際上并不能很好解決問題,它的本質還是線性遍歷的,因為你要使用目錄,但是前置條件是你能遍歷到目錄的那個page,這不還是鏈表的遍歷嗎?

所以在page內已有目錄的基礎之上,我們的解決思路也是簡單的,我們給page也指定目錄,也就是說在數據有目錄的基礎之上,我們對不同的page,也指定上目錄,圖大致是這樣的:

這樣,就能通過第一層目錄,快速定位到對應數據的page的附近,然后遍歷到page再次通過目錄,快速定位到數據的附近,運氣好可以直接命中,這效率提高的就非常多了。

那么現在就可以回答剛才的問題了:MySQL為什么要自己排序?因為目錄指代的順序不能錯亂了,錯亂了目錄無法正確定位。

現在還可以回答一個問題,為什么要使用主鍵約束?不使用可以嗎?

答案是可以,如果我們不使用主鍵,也沒有唯一鍵和Not null的列,InnoDB 會自動生成一個隱藏的6字節的row id作為主鍵,并建立聚簇索引。

所以實際上的page的組織結構就是這樣的,反正遍歷某個目錄的時候,如果太花時間了,再來一個目錄就行,而如果我們仔細一看這個結構,不就是傳說中的B+樹嗎!!所以得出結論,存儲引擎innnodb采取的索引方式是B+樹。那么其他結構為什么不行,比如hash,它不支持范圍查找,因為查找它需要映射,比如紅黑樹,紅黑樹和AVL樹都是近似平衡或者絕對平衡,這就意味著每一層都會有節點,也就是說它很高,而B+樹是矮胖的,層高越低,和系統磁盤交互的次數就很少,所以紅黑樹也pass,至于二叉平衡樹就不用說了。

特性 / 數據結構B+樹紅黑樹AVL樹Hash
是否支持范圍查詢? 是,效率高(葉子節點有序且鏈表連接)? 不直接支持(需中序遍歷)? 不直接支持? 不支持
節點扇出(分支數量)高(每頁存多個key)低(每節點2個子節點)N/A
樹高度(影響IO次數)矮(log?N)較高(log?N)更高(比紅黑樹高)無樹結構
適合數據規模大數據量、磁盤存儲小數據量、內存結構小數據量、內存結構精確查詢、大數據量
插入/刪除性能中等,代價為平衡和分裂快速,近似平衡較慢(頻繁旋轉)快(無排序要求)
是否順序存儲? 葉子節點有序、鏈表相連? 否? 否? 否
磁盤友好性? 高,每個節點可對應一個磁盤頁? 差,節點少無法利用頁空間? 差? 差
典型應用場景數據庫索引(如 InnoDB)操作系統調度/平衡集合編譯器語法樹等緩存(如 Redis)、哈希索引

重談page

前文我們提及,MySQL的Innodb是通過page這個基本單位來和磁盤進行交互的,那么在如果構建索引的部分,我們發現這個基本單位里面有前后指針,有目錄結構,有各種數據,那么交互的單位真的單純只是一個內存塊的話,是不太能執行這樣的操作,所以對于16KB,我們仍然是:先描述再組織

即對于每個page都要存入相關信息,然后在buffer pool里面對它們進行一個管理,buffer pool就是MySQL的存儲引擎Innodb的緩沖區,我們也可以在MySQL的配置文件里看到,如果有的時候沒看到也不代表它沒有,MySQL有對應的默認行為的。

那么重歸到Page里面,我們可以非常非常粗略地認為page是這樣的:

struct InnoDBPage {byte file_header[38];       // 文件頭byte page_header[56];       // 頁頭信息Record infimum;             // 最小記錄Record supremum;            // 最大記錄Record user_records[];      // 用戶插入的數據記錄byte free_space[];          // 空閑空間byte page_directory[];      // 槽指針(指向記錄)byte file_trailer[8];       // 文件尾部校驗InnoDBPage* next;InnoDBpage* prev;
};

所以MySQL中的索引,實際上是某個存儲引擎,基于某種特定的組織方式進行的數據排列而已。

聚簇索引VS非聚簇索引

根據上文的描述,我們很明顯能夠發現Innodb的索引的數據本身都是放在最后一層的,目錄都是在其他層,這種數據本身和目錄放在統一結構的索引,我們稱為聚簇索引,對于MyIsam來說,它同樣使用的是B+樹作為索引,但是它和Innodb不同的是它的葉節點存儲的是數據記錄的地址

也就是說MyIsam的葉結構指向的是數據的地址,那么它的數據本身沒有和目錄放在一起,所以哦我們稱呼這種索引為非聚簇索引

回表查詢

對于主鍵索引來說,我們發現一個點就是,索引好像只有主鍵才有?那么我們查其他字段怎么辦?能不能給其他列建立一個輔助索引?

當然是可以的,我們可以通過命令create index index_name on table_name(對應列名)來創建對應的輔助索引,就像這樣:

然后我們就可以通過show index from test_3查看對應的索引信息了:

在 InnoDB 中,如果我們通過輔助索引(二級索引)來查詢數據,且查詢的字段不在輔助索引中,就會發生“回表操作”。
假設有三個列:A(主鍵)、B、C,其中我們對 C 建了索引,A 是主鍵。
當我們執行 SELECT B FROM test_table WHERE C = 'xxx' 這個語句時:

  1. MySQL 會先通過 idx_C(C 列上的輔助索引)定位滿足條件的記錄的主鍵 A;

  2. 再通過主鍵 A 回到聚簇索引中查找完整的數據行,拿到 B 的值。

因為在 InnoDB 中,輔助索引結構只包含:被索引的列 + 主鍵列,不包含其它字段,所以我們必須“回表”才能拿到未包含的字段(如 B)。

優點缺點
利用輔助索引快速定位主鍵回表需要額外查詢,性能下降
輔助索引占用空間小增加查詢的 I/O 操作
支持多種查詢條件查詢字段不在索引中時必須回表

我們前文提及了索引的作用就是為了減少系統的IO次數,那么,回表無疑增加了IO次數,所以我們可以通過覆蓋索引解決回表查詢的缺點:

CREATE TABLE test (id INT PRIMARY KEY,name VARCHAR(50),age INT,email VARCHAR(100),INDEX idx_name_age (name, age)
);
SELECT name, age FROM test WHERE name = 'Tom';

即我們讓查詢的數據都在一起,不去主鍵索引里面找就可以了。那么針對于輔助索引來說Innodb是不會所有的葉子節點額外加數據的,因為太浪費空間了

不過如果使用了覆蓋索引,我們查詢的時候會看到兩個索引,但是實際上是一個:


?索引分類

索引分為主鍵索引,唯一鍵索引,普通索引,全文索引。其中普通索引我們已經通過回表查詢簡單的介紹了,其實普通索引有多種創建方式分別是:

create table user8(id int primary key,name varchar(20),email varchar(30),index(name) --在表的定義最后,指定某列為索引
);
create table user9(
id int primary key,
name varchar(20),
email varchar(30));
alter table user9 add index(name); --創建完表以后指定某列為普通索引
create table user10(
id int primary key,
name varchar(20),  
email varchar(30));-- 創建一個索引名為 idx_name 的索引    
create index idx_name on user10(name);

這種方式都可以成功創建一個輔助索引。

而對于主鍵索引和唯一鍵索引我們可以理解為:創建主鍵索引和唯一鍵索引實際上是創建主鍵和唯一鍵,不管是哪種方式,創建表的時候就指定了主鍵或者創建完之后新增主鍵,Innodb都會創建對應的索引。

不過實際上唯一鍵索引它就是輔助索引,不過多了唯一性約束而已。

類型是否唯一是否聚簇自動建索引特點
主鍵(PRIMARY)? 是? 是? 是一張表只能有一個,行按主鍵排序存儲
唯一鍵(UNIQUE)? 是? 否? 是可有多個,索引中也包含主鍵列作為引用
普通索引(INDEX)? 否? 否手動創建可有多個,用于優化查詢

刪除索引的操作有三種,一種是直接刪除對應的主鍵,唯一鍵等。一種是alter table tablename drop index indexname.一種是drop index name on tablename。

對于全文索引這里不做討論。


感謝閱讀!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/81544.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/81544.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/81544.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MySQL——7、復合查詢和表的內外連接

復合查詢和表的內外連接 1、基本查詢回顧2、多表查詢3、自連接4、子查詢4.1、單行子查詢4.2、多行子查詢4.3、多列子查詢4.4、在from子句中使用子查詢4.5、合并查詢 5、表的內連和外連5.1、內連接5.2、外連接5.2.1、左外連接5.2.2、右外連接 1、基本查詢回顧 1.1、查詢工資高于…

MYSQL故障排查和環境優化

一、MySQL故障排查 1. 單實例常見故障 (1)連接失敗類問題 ERROR 2002 (HY000): Cant connect to MySQL server 原因:MySQL未啟動或端口被防火墻攔截。 解決:啟動MySQL服務(systemctl start mysqld)或開放…

7GB顯存如何部署bf16精度的DeepSeek-R1 70B大模型?

構建RAG混合開發---PythonAIJavaEEVue.js前端的實踐-CSDN博客 服務容錯治理框架resilience4j&sentinel基礎應用---微服務的限流/熔斷/降級解決方案-CSDN博客 conda管理python環境-CSDN博客 快速搭建對象存儲服務 - Minio,并解決臨時地址暴露ip、短鏈接請求改…

數字圖像處理——圖像壓縮

背景 圖像壓縮是一種減少圖像文件大小的技術,旨在在保持視覺質量的同時降低存儲和傳輸成本。隨著數字圖像的廣泛應用,圖像壓縮在多個領域如互聯網、移動通信、醫學影像和衛星圖像處理中變得至關重要。 技術總覽 當下圖像壓縮JPEG幾乎一統天下&#xff…

抖音視頻怎么去掉抖音號水印

你是不是經常遇到這樣的煩惱?看到喜歡的抖音視頻,想保存下來分享給朋友或二次創作,卻被抖音號水印擋住了畫面?別著急,今天教你幾種超簡單的方法,輕松去除水印,高清無水印視頻一鍵保存&#xff0…

RISC-V 開發板 MUSE Pi Pro PCIE 測試以及 fio 崩潰問題解決

視頻講解: RISC-V 開發板 MUSE Pi Pro PCIE 測試以及 fio 崩潰問題解決 板子上有一個m.2的pcie插槽,k1有三個pcie控制器,pcie0和usb3復用一個phy,所以實際開發板就兩個,測試的話,上一個nvme硬盤&#xff0c…

超級管理員租戶資源初始化與授權管理設計方案

背景說明 在多租戶系統中,資源(如功能模塊、系統菜單、服務能力等)需按租戶維度進行授權管理。超級管理員在創建新租戶時,需要初始化該租戶的資源授權信息。 兩種可選方案 方案描述方案 A:前端傳入選中的資源列表創…

stm32week16

stm32學習 十一.中斷 4.使用中斷 EXTI的配置步驟: 使能GPIO時鐘設置GPIO輸入模式使能AFIO/SYSCFG時鐘設置EXTI和IO對應關系設置EXTI屏蔽,上/下沿設置NVIC設計中斷服務函數 HAL庫的使用: 使能GPIO時鐘:__HAL_RCC_GPIOx_CLK_EN…

什么是RDMA?

什么是RDMA? RDMA(RemoteDirect Memory Access)技術全稱遠程直接內存訪問,就是為了解決網絡傳輸中服務器端數據處理的延遲而產生的。它將數據直接從一臺計算機的內存傳輸到另一臺計算機,無需雙方操作系統的介入。這允許高吞吐、低延遲的網絡…

golang 安裝gin包、創建路由基本總結

文章目錄 一、安裝gin包和熱加載包二、路由簡單場景總結 一、安裝gin包和熱加載包 首先終端新建一個main.go然后go mod init ‘項目名稱’執行以下命令 安裝gin包 go get -u github.com/gin-gonic/gin終端安裝熱加載包 go get github.com/pilu/fresh終端輸入fresh 運行 &…

【數據結構篇】鏈式結構二叉樹

目錄: 一 二叉鏈的概念與結構: 1.1 概念: 1.2 結構: 二 二叉鏈的實現: 2.1 二叉樹的構建: 2.2 二叉樹的遍歷: 2.2.1 前序遍歷: 2.2.2 中序遍歷: 2.2.3 后序遍歷…

【MySQL】02.數據庫基礎

1. 數據庫的引入 之前存儲數據用文件就可以了,為什么還要弄個數據庫? 文件存儲存在安全性問題,文件不利于數據查詢和管理,文件不利于存儲海量數據,文件在程序中控制不方便。而為了解決上述問題,專家們設計出更加利于…

什么是 Langchain 以及其核心組件

LangChain 官方文檔:LangChain 一、什么是Langchain LangChain 是一個用于構建基于LLM的應用框架,它提供了對 LLM API 的封裝和擴展,使開發者能夠更方便地構建復雜的應用。 個人理解:用類比的方法來說,LangChain類似…

博客系統功能測試

博客系統網址:http://8.137.19.140:9090/blog_list.html 主要測試內容 功能測試、界面測試、性能測試、易用性測試、安全測試、兼容性測試、弱網測試、安裝卸載測試、壓力測試… 測試方法及目的 利用selenium和python編寫測試腳本,對博客系統進行的相關…

項目制作流程

一、使用 CRA 創建項目 npx create-react-app name 二、按照業務規范整理項目目錄 (重點src目錄) 三、安裝插件 npm install sass -Dnpm install antd --savenpm install react-router-dom 四、配置基礎路由 Router 1. 安裝路由包 react-router-dom …

ngx_http_random_index_module 模塊概述

一、使用場景 隨機內容分發 當同一目錄下存放多份等價內容(如多張輪播圖、不同版本靜態頁面等)時,可通過隨機索引實現負載均衡或流量分散。A/B 測試 通過目錄請求自動隨機分配用戶到不同測試組,無需后端邏輯參與。動態“首頁”選…

智能權限守護者:基于Python描述符的動態角色控制實現

智能權限守護者:基于Python描述符的動態角色控制實現 引言:當描述符遇見權限管理 在Python的魔法方法體系中,描述符(Descriptor)以其優雅的屬性訪問控制機制著稱。當我們將描述符與RBAC(基于角色的訪問控制)模型結合,就能創造出既靈活又安全的動態權限管理系統。本文…

Linux 的 UDP 網絡編程 -- 回顯服務器,翻譯服務器

目錄 1. 回顯服務器 -- echo server 1.1 相關函數介紹 1.1.1 socket() 1.1.2 bind() 1.1.3 recvfrom() 1.1.4 sendto() 1.1.5 inet_ntoa() 1.1.6 inet_addr() 1.2 Udp 服務端的封裝 -- UdpServer.hpp 1.3 服務端代碼 -- UdpServer.cc 1.4 客戶端代碼 -- UdpClient.…

Linux 內核等待機制詳解:prepare_to_wait_exclusive 與 TASK_INTERRUPTIBLE

1. prepare_to_wait_exclusive 函數解析 1.1 核心作用 prepare_to_wait_exclusive 是 Linux 內核中用于將進程以獨占方式加入等待隊列的關鍵函數,其主要功能包括: 標記獨占等待:通過設置 WQ_FLAG_EXCLUSIVE 標志,表明此等待條目是獨占的。 安全入隊:在自旋鎖保護下,將條…

【Android構建系統】了解Soong構建系統

背景介紹 在Android7.0之前,Android使用GNU Make描述和執行build規則。Android7.0引入了Soong構建系統,彌補Make構建系統在Android層面變慢、容易出錯、無法擴展且難以測試等缺點。 Soong利用Kati GNU Make克隆工具和Ninja構建系統組件來加速Android的…