【MySQL基礎】MySQL索引全面解析:從原理到實踐

MySQL學習:

https://blog.csdn.net/2301_80220607/category_12971838.html?spm=1001.2014.3001.5482

前言:

在前面我們基本上已經把MySQL的基礎知識都進行了學習,但是我們之前處理的數據都是十分少的,但是如果當我們的數據量很大的時候,比如一張表中有一百萬個數據,我們要對其中一個進行查詢的時候,效率就會很慢了,這個時候我們就可以借助索引來幫我們完成提高效率的工作,索引簡單點來說就是將數據以特定的數據結構組織起來,從而方便查詢和管理

1. 索引的概念和作用

1.1 索引的概念

索引是數據庫中一種特殊的數據結構,它類似于書籍的目錄,能夠幫助數據庫系統快速定位到表中的特定數據,而不必掃描整個表。索引本質上是通過額外的數據結構(如B+樹、哈希表等)對表中的一個或多個列的值進行排序和組織,從而加速數據檢索操作。

1.2 索引的作用

索引可以提高數據庫的性能,而且索引不用加內存,不用改程序,不用調sql,只需要執行正確的創建索引語句,就可以很方便的幫助我們在大量的數據中進行查找工作,它的本質就是通過特定的數據結果對數據庫中的數據進行管理,主要是通過B+樹

1.3 索引的局限

沒有什么東西是百利而無一弊的,索引雖然可以提高我們的查詢速度,但是插入、更新、和刪除的速度降低為代價的,因為本來這些操作就涉及大量的IO操作,索引的創建會增加IO操作次數,IO操作會大大影響這些操作的效率。同時索引的創建還會消耗額外的內存空間。

但是對于擁有海量數據的數據庫,索引的創建仍是很有必要的。

2. 索引的底層數據結構

關于索引的使用其實還是比較簡單的,關鍵是我們需要明白索引的實現原理是什么,這里我們就講一下索引的底層實現

2.1 MySQL與磁盤交互基本單位

在計算機硬盤的實現中,硬盤是由多個扇面組成的,每個扇面上又被劃成不同的扇區,每個扇區的大小都是512字節,即我們存儲在磁盤中的內容都是以512字節作為存儲單元的。

那這是不是意味著我們所有的I/O操作都是以512字節為單位的呢?

答案其實是否定的。不同的服務進行I/O操作的單位其實是不同的,比如我們的MySQL的操作單位是16KB,那為什么不是512字節而是16KB呢?這其實與MySQL服務的所在層級有關

如圖,MySQL服務實際上是作為一個服務進程在應用層跑動的,所以MySQL并不是直接與磁盤或內存進行交互的,它是通過操作系統(OS)提供的接口與磁盤進行數據的傳送的,雖然磁盤的操作單位為512字節,但是MySQL服務綜合考慮速度、容量等各方面因素,它所選擇的操作單位是16KB

實現方法就是:在操作系統層次和MySQL服務應用層上實際上都有一個文件緩沖區的存在,MySQL服務寫入的內容在將buffer pool寫滿之后傳給操作系統,操作系統再將這部分內容傳給磁盤;同理,磁盤的操作也是這樣的,磁盤將MySQL服務所需數據傳給操作系統,操作系統再將數據傳給MySQL服務

這個基本的存儲單元就叫做MySQL的頁

還遺漏了一個重要的知識點是在MySQL的底層實現中,不同的存儲引擎的實現是不同的,但是不同的地方主要體現在頁的管理上,上面的內容基本上實現還都是一樣的

2.2 建立共識

  • MySQL中的數據文件,是以page為單位保存在磁盤中的
  • MySQL的CURD操作,都是通過計算,找到對應的插入位置,或者找到對應的要查詢或修改的數據
  • 只要涉及到運算,就需要CPU參與,為了方便CPU的參與,一定要先將數據移動到內存中
  • 所以在特定的時間內,數據一定是磁盤中有,內存中也有。后續操作完內存數據之后,以特定的刷新策略,刷新到磁盤。而這時,就涉及到磁盤和內存的數據交互,也就是IO了。而此時IO的基本單位就是Page
  • 為了更好的進行上面的操作, MySQL 服務器在內存中運行的時候,在服務器內部,就申請了被稱為 BufferPool 的的大內存空間,來進行各種緩存。其實就是很大的內存空間,來和磁盤數據進行IO交互。
  • 為了提高效率,一定要盡可能的減少系統和磁盤IO的次數

2.3 索引的理解

建立測試表:

create table if not exists user (
id int primary key, --一定要添加主鍵哦,只有這樣才會默認生成主鍵索引
age int not null,
name varchar(16) not null
);

插入多條測試記錄:

--插入多條記錄,注意,我們并沒有按照主鍵的大小順序插入哦
mysql> insert into user (id, age, name) values(3, 18, '楊過');
Query OK, 1 row affected (0.01 sec)mysql> insert into user (id, age, name) values(4, 16, '小龍女');
Query OK, 1 row affected (0.00 sec)mysql> insert into user (id, age, name) values(2, 26, '黃蓉');
Query OK, 1 row affected (0.01 sec)mysql> insert into user (id, age, name) values(5, 36, '郭靖');
Query OK, 1 row affected (0.00 sec)mysql> insert into user (id, age, name) values(1, 56, '歐陽鋒');
Query OK, 1 row affected (0.00 sec)

查看插入結果:

select * from user;

觀察這個插入結果,我們可以發現,我們并沒有按照id順序來插入數據,但是最后卻發現插入后的數據是按照id來排序的,id是主鍵,其實原因是建表時被設置為主鍵的列會默認建立索引,索引通過這樣排序的方式就可以幫助我們更快的查找到我們想要的數據

但是僅僅知道這些還是不夠的,我們需要知道索引工作的原理,下面我們就來看一下

2.4 索引的底層實現

關于page的認識

上面我們講過MySQL與磁盤的交互單位為page(16KB),但是為什么呢?為什么不用多少,加載多少呢?

這其實是為了提高效率,比如上面我們插入的數據,我們現在如果要查找id=1的記錄,按照用多少取多少的方式,我們就需要直接把它從磁盤加載到MySQL服務端,進行依次I/O操作,如果又想查id=2的記錄,就有需要再進行這樣一次的I/O

需要注意的是這樣的I/O操作在計算機運行中,會消耗大量的空間,所以為了提高效率我們必須想辦法減少I/O操作的次數,所以我們就可以一次直接I/O更多的數據(page),比如把五條記錄全部取了,這樣不管要哪條記錄,我們的服務端都可以直接在自己的緩沖區中找就可以了,這樣就節省的大量的時間

當然并不是每次要取的數據都能在同一個page頁,但是根據局部性原理,還是能夠保證我們在大部分情況下都是效率更高的

理解單個page

上面講了page的概念后,實際上我們就應該認識到page作為MySQL的存儲單元一定會伴隨著許多的設計的。

MySQL中有很多表,這些表中存放著大量的數據,我們可以理解成這些表中的數據是存放在一個或多個page中的,由于大量page表的存在,所以我們需要對page進行組織管理

如上,就是page的基本構成,page中的數據是以鏈表的形式存放的,同時page自身也是通過鏈表的形式進行組織的,它里面有兩個指針分別指向前一個page和后一個page

理解多個page
  • 通過上面的分析,我們知道,上面頁模式中,只有一個功能,就是在查詢某條數據的時候直接將一整頁的數據加載到內存中,以減少硬盤IO次數,從而提高性能。但是,我們也可以看到,現在的頁模式內部,實際上是采用了鏈表的結構,前一條數據指向后一條數據,本質上還是通過數據的逐條比較來取出特定的數據。
  • 如果有 1 千萬條數據,一定需要多個 Page 來保存 1 千萬條數據,多個 Page 彼此使用雙鏈表鏈接起來,而且每個 Page內部的數據也是基于鏈表的。那么,查找特定一條記錄,也一定是線性查找。這效率也太低了。
頁目錄

現在有一本書,我們找到其中某些內容的時候,一定是先看目錄,找到這些內容對應的頁數,然后再根據頁數再去找這部分內容,這樣可以幫助我們節省很多時間。

頁目錄會占用幾頁,這是一種空間換時間的做法,但是仍然是十分值得去做的

page頁目錄

為了方便我們的page進行高效查找,我們可以對page頁也進行添加目錄操作,即可以添加在page內對page中內容進行管理,也可以添加在page外對page進行管理

添加在page內

那么當前,在一個Page內部,我們引入了目錄。比如,我們要查找id=4記錄,之前必須線性遍歷4次,才能拿到結果。現在直接通過目錄2[3],直接進行定位新的起始位置,提高了效率。現在我們可以再次正式回答上面的問題了,為何通過鍵值 MySQL 會自動排序?

  • 可以很方便引入目錄

添加在page外

MySQL 中每一頁的大小只有 16KB ,單個Page大小固定,所以隨著數據量不斷增大, 16KB 不可能存下所有的數據,那么必定會有多個頁來存儲數據。

我們前面講過page頁中會有兩個指針的,這兩個指針就是幫助我們建立這樣的雙鏈表的結構的

在單表數據不斷被插入的情況下, MySQL 會在容量不足的時候,自動開辟新的Page來保存新的數據,然后通過指針的方式,將所有的Page組織起來。
需要注意,上面的圖,是理想結構,大家也知道,目前要保證整體有序,那么新插入的數據,不一定會在新Page上面,這里僅僅做演示。
這樣,我們就可以通過多個Page遍歷,Page內部通過目錄來快速定位數據。可是,貌似這樣也有效率問題,在Page之間,也是需要 MySQL 遍歷的,遍歷意味著依舊需要進行大量的IO,將下一個Page加載到內存,進行線性檢測。這樣就顯得我們之前的Page內部的目錄,有點杯水車薪了。

那么解決方法是什么呢?解決方法,其實就是我們之前的思路,給page也帶一個目錄

存在一個目錄頁來管理頁目錄,目錄頁中的數據存放的就是指向的那一頁中最小的數據。有數據,就可通過比較,找到該訪問那個Page,進而通過指針,找到下一個Page。

其實目錄頁的本質也是頁,普通頁中存的數據是用戶數據,而目錄頁中存的數據是普通頁的地址
可是,我們每次檢索數據的時候,該從哪里開始呢?雖然頂層的目錄頁少了,但是還要遍歷啊?不用擔心,可以在加目錄頁

我們觀察就可以發現這就是數據結構中的B+樹啊!!至此,我們就給我們的user表建立了主鍵索引。現在隨便找一個id=?的數據,我們會發現查詢速度會快很多。

復盤一下
  • Page分為目錄頁和數據頁。目錄頁只放各個下級Page的最小鍵值。
  • 查找的時候,自頂向下找,只需要加載部分目錄頁到內存,即可完成算法的整個查找過程,大大減少了IO次數

以上就是索引的底層實現,但是需要注意的是我們講的這種B+樹的底層實現方式,適用的主要是MyISAM存儲引擎,不同的存儲引擎的底層實現可能是不同的,比如我們還常用的另一種存儲引擎InnoDB就是常用B樹來作為底層數據結構,用B樹實現的存儲引擎它的用戶數據和索引數據不會分離,被稱為聚簇索引;而用B+樹實現的索引類型一般為非聚簇索引

3. 索引操作

3.1 創建索引

3.1.1 創建主鍵索引
  • 第一種方法
-- 在創建表的時候,直接在字段名后指定 primary key
create table user1(id int primary key, name varchar(30));
  • 第二種方法
-- 在創建表的最后,指定某列或某幾列為主鍵索引
create table user2(id int, name varchar(30), primary key(id));
  • 第三種方法
create table user3(id int, name varchar(30));
-- 創建表以后再添加主鍵
alter table user3 add primary key(id);

主鍵索引的特點:

  • 一個表中,最多有一個主鍵索引,當然可以使符合主鍵
  • 主鍵索引的效率高(主鍵不可重復)
  • 創建主鍵索引的列,它的值不能為null,且不能重復
  • 主鍵索引的列基本上是int
3.1.2?創建唯一索引
  • 第一種方式
-- 在表定義時,在某列后直接指定unique唯一屬性。
create table user4(id int primary key, name varchar(30) unique);
  • 第二種方式
-- 創建表時,在表的后面指定某列或某幾列為unique
create table user5(id int primary key, name varchar(30), unique(name));
  • 第三種方式
create table user6(id int primary key, name varchar(30));
alter table user6 add unique(name);

唯一索引的特點:

  • 一個表中,可以有多個唯一索引
  • 查詢效率高
  • 如果在某一列建立唯一索引,必須保證這列不能有重復數據
  • 如果一個唯一索引上指定not null,等價于主鍵索引
3.1.3?創建普通索引
  • 第一種方式
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);

普通索引的創建:

  • 一個表中可以有多個普通索引,普通索引在實際開發中用的比較多
  • 如果某列需要創建索引,但是該列有重復的值,那么我們就應該使用普通索引
3.1.4?創建全文索引

當對文章字段或有大量文字的字段進行檢索時,會使用到全文索引。MySQL提供全文索引機制,但是有要求,要求表的存儲引擎必須是MyISAM,而且默認的全文索引支持英文,不支持中文。如果對中文進行全文檢索,可以使用sphinx的中文版(coreseek)。

CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title,body)
)engine=MyISAM;INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...'),
('How To Use MySQL Well','After you went through a ...'),
('Optimizing MySQL','In this tutorial we will show ...'),
('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
('MySQL vs. YourSQL','In the following database comparison ...'),
('MySQL Security','When configured properly, MySQL ...');
  • 查找有沒有database數據

如果使用如下查詢方式,雖然查詢出數據,但是沒有使用到全文索引

select * from articles where body like '%database%';

可以用explain工具看一下,是否使用到索引

explain select * from articles where body like '%database%'\G

  • 如何使用全文索引
SELECT * FROM articles
-> WHERE MATCH (title,body) AGAINST ('database');

通過explain來分析這個sql語句

explain SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('database')\G

3.2 查詢索引

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

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

相關文章

第三十五章 I2S——音頻傳輸接口

第三十五章 I2S——音頻傳輸接口 目錄 第三十五章 I2S——音頻傳輸接口 1 I2S概述 1.1 簡介 1.2 功能特點 1.3 工作原理 1.4 利用DMA通信的I2S 1.4.1 I2S配合DMA通信工作原理 1.4.2 配置要點 2 應用場景 2.1 消費類音頻設備 2.2 專業音頻設備 2.3 通信設備 2.4 汽車電子 2.5 嵌…

產品-Figma(英文版),圖像的布爾類型圖例說明

文章目錄 Union SelectionSubtract SelectionIntersect SelectionExclude SelectionFlatten Selection Union Selection 把多個形狀合并成一個新的完整形狀,保留所有外部輪廓,內部不被切割。由于紅色的長方形在外面的一層,所以切割后&#x…

Windows CMD命令分類大全

?? ?一、系統與磁盤管理? ?系統信息? systeminfo:查看詳細硬件及系統配置(版本/內存/補丁)211 winver:快速檢查Windows版本11 msinfo32:圖形化系統信息面板811?磁盤工具? chkdsk /f:修復磁盤錯誤&…

【Dify系列】【Dify1.4.2 升級到Dify1.5.0】

1. 升級前準備工作 1.1 數據備份: 進入原安裝包 docker 目錄,備份“volumes”文件夾,此文件夾包含了 Dify 數據庫數據: rootjoe:/usr/local/dify/docker/volumes# pwd /usr/local/dify/docker/volumesrootjoe:/usr/local/dify/…

DeepSeek網頁版隨機點名器

用DeepSeek幫我們生成了一個基于html5的隨機點名器,效果非常棒,如果需要加入名字,請在代碼中按照對應的格式添加即可。 提示詞prompt 幫我生成一個隨機點名的HTML5頁面 生成真實一點的名字數據 點擊隨機按鈕開始隨機選擇 要有閃動的效果 &…

前后端分離實戰2----后端

戳我抵達前端 項目描述:用Vscode創建Spring Bootmybatis項目,用maven進行管理。創建一個User表,對其內容進行表的基本操作(增刪改查),顯示在前端。 項目地址:戳我一鍵下載項目 運行效果如下&…

深入 ARM-Linux 的系統調用世界

1、引言 本篇文章以 ARM 架構為例,進行講解。需要讀者有一定的 ARM 架構基礎 在操作系統的世界中,系統調用(System Call)是用戶空間與內核空間溝通的橋梁。用戶態程序如 ls、cp 或你的 C 程序,無權直接操作硬件、訪問文…

LabVIEW鍵盤鼠標監測控制

通過Input Device Control VIs,實現對鍵盤和鼠標活動的監測。通過AcquireInput Data VI 在循環中持續獲取輸入數據,InitializeKeyboard與InitializeMouse VIs 先獲取設備ID 引用,用于循環內監測操作;運行時可輸出按鍵信息&#xf…

Linux 系統管理:自動化運維與容器化部署

在現代 IT 基礎設施中,自動化運維和容器化部署是提高系統管理效率和可維護性的關鍵。Linux 系統因其穩定性和靈活性而被廣泛應用于服務器和數據中心。本文將深入探討 Linux 系統管理中的自動化運維和容器化部署技術,幫助系統管理員實現高效運維和快速部署…

直播 APP 開發需要多少成本

直播行業的火爆催生了大量直播 APP 開發需求,而開發成本是開發者最關注的問題之一。其成本構成復雜,受功能需求、開發方式、技術難度等多種因素影響。? 基礎功能開發是成本的重要組成部分。用戶注冊登錄、直播間創建與管理、視頻播放、聊天互動等功能開…

Reactor操作符的共享與復用

在 Reactor 中,transform 和 transformDeferred 是兩個用于代碼復用和操作符鏈封裝的高級操作符。它們允許你將一組操作符封裝成一個函數,并在適當的時候應用到響應式流中。以下是它們的詳細總結: 1. transform 操作符 作用:tran…

C#中的Converter詳解

Converter是C#中一個非常有用的概念,主要用于類型轉換。它通常以委托或接口的形式出現,允許開發者定義如何將一種類型轉換為另一種類型。下面我將詳細介紹Converter的概念、使用場景,并以布爾型轉換為例展示具體應用。 Converter的基本概念 …

LabVIEW熒光微管圖像模擬

利用LabVIEW平臺,集成 PI 壓電平臺、Nikon 熒光顯微鏡及Andor sCMOS 相機等硬件,構建熒光微管滑行實驗圖像序列模擬系統。通過程序化模擬微管運動軌跡、熒光標記分布及顯微成像過程,為生物醫學領域微管跟蹤算法測試、運動特性分析提供標準化仿…

CentOS下Nginx服務器搭建全攻略

Nginx 安裝與配置完整指南 一、安裝 Nginx 1.1 添加 Nginx 官方倉庫 在 CentOS 系統中,默認倉庫的 Nginx 版本可能較舊(通常為 1.12 或更早版本),建議添加官方倉庫來安裝最新穩定版本(目前為 1.25.x)&am…

網絡拓撲圖繪制全流程:從架構解析到工具實戰

在數據呈現與系統管理中,清晰展示設備間的邏輯關系至關重要。網絡拓撲圖正是這樣一種有效的可視化工具。它通過節點設備和連接線路,直觀呈現網絡結構或項目流程中各元素的布局與交互關系,幫助理解系統運作、診斷問題并確保項目順利進行。 1. …

Git 簡介安裝教程

📢歡迎點贊👍收藏?留言📝如有錯誤敬請指正! 目錄 一、Git 的安裝1.1 Git 的下載1.2 Git 的安裝1.2.1 使用許可聲明1.2.2 選擇安裝目錄1.2.3 選擇安裝組件1.2.4 選擇開始菜單文件夾1.2.5 選擇 Git 默認編輯器1.2.6 決定初始化新項…

鴻蒙NEXT-鴻蒙三層架構搭建,嵌入HMRouter,實現便捷跳轉,新手攻略。(2/3)

在上一小節我們已經完成了關于三層架構的搭建,接下來我們來實現在三層架構中的導入依賴,將他們相互聯系起來。 第一步:在features產品定制層中,對其中所有的動態共享包導入依賴,示例features>my>oh-package.jso…

【每天一個知識點】語料投毒(Corpus Poisoning)

“語料投毒”(Corpus Poisoning) 是指攻擊者通過向大型語言模型(如 ChatGPT)使用的外部知識庫中注入惡意或誤導性文檔,從而干擾模型的檢索與回答過程,導致其輸出錯誤、虛假或有害內容。 🔍 舉個…

疏通經脈: Bridge 聯通邏輯層和渲染層

本節概述 經過前面兩節的開發,我們已經完成了小程序邏輯線程和 UI 線程的啟動引擎準備,這節開始,我們將完善 native bridge 層的搭建,構建起邏輯線程和UI線程之間的橋梁。 開始之前我們先來回顧一下邏輯引擎小節相關的流程圖: 一…

【攻防篇】解決:阿里云docker 容器中自動啟動xmrig挖礦

解決:阿里云服務器docker容器被植入挖礦程序 **1. 緊急處理:停止挖礦進程****(1)查找并終止 xmrig 進程****(2)刪除惡意文件** **2. 清理被感染的容器****(1)停止并刪除容器****&…