MySQL的索引操作及底層結構淺析

一.索引

? ? ? ? 提高數據庫的性能,索引是物美價廉的東西了。不用加內存,不用改程序,不用調sql,只要執行正確的 create index ,查詢速度就可能提高成百上千倍。但是天下沒有免費的午餐,查詢速度的提高是以插入、更新、刪除的速度為代價的,這些寫操作,增加了大量的IO。所以它的價值,在于提高一個海量數據的檢索速度。

1.1mysql與磁盤交互的基本單位

? ? ? ? 內存與磁盤的基本交互單位通常為頁(如4KB),訪問一個字節時,若其所在頁未加載,操作系統會將該頁全部加載到內存,這體現了空間局部性原理。

? ? ? ? 而 MySQL 作為一款應用軟件,可以想象成一種特殊的文件系統。它有著更高的IO場景,所以,為了提高基本的IO效率, MySQL 進行IO的基本單位是 16KB.

我們可以使用SHOW GLOBAL STATUS LIKE 'innodb_page_size';來查看當前mysql的IO基本交互單位的大小(我們后面統一基于innodb進行講解):

????????磁盤這個硬件設備的基本單位是 512 字節,而 MySQL InnoDB引擎 使用 16KB 進行IO交互。即, MySQL 和磁盤進行數據交互的基本單位是 16KB 。這個基本數據單元,在 MySQL 這里叫做page(注意和系統的page區分).

我們先來理清下mysql進行IO的過程:

  • MySQL 中的數據文件,是以page為單位保存在磁盤當中的。
  • MySQL 的 CURD 操作,都需要通過計算,找到對應的插入位置,或者找到對應要修改或者查詢的數據。
  • 而只要涉及計算,就需要CPU參與,而為了便于CPU參與,一定要能夠先將數據移動到內存當中。所以在特定時間內,數據一定是磁盤中有,內存中也有。后續操作完內存數據之后,以特定的刷新策略,刷新到磁盤。而這時,就涉及到磁盤和內存的數據交互,也就是IO了。而此時IO的基本單位就是Page。

????????為了更好的進行上面的操作, MySQL 服務器在內存中運行的時候,在服務器內部,就申請了被稱為 Buffer Pool 的的大內存空間,來進行各種緩存。其實就是很大的內存空間,來和磁盤數據進行IO交互。

理解一下什么是隨機訪問與連續訪問:
隨機訪問:本次IO所給出的扇區地址和上次IO給出扇區地址不連續,這樣的話磁頭在兩次IO操作之間需要作比較大的移動動作才能重新開始讀/寫數據。

連續訪問:如果當次IO給出的扇區地址與上次IO結束的扇區地址是連續的,那磁頭就能很快的開始這次IO操作,這樣的多個IO操作稱為連續訪問。

? ? ? ??對于沒有索引的查詢,必須加載整個表的數據頁到Buffer Pool,在沒有索引的情況下查找特定數據會產生大量隨機磁盤訪問.非常的耗時間,如何更加高效的利用mysql的buffer pool呢?索引的作用正是為了解決這些問題.我們先來看看索引是什么:

1.2索引的理解

????????常見的索引有主鍵索引,唯一鍵索引,普通索引與全文索引(主鍵索引和唯一鍵索引只要我們創建了相對應的鍵便會自動基于鍵列生成索引).我們之前說過,表在不給主鍵的時候,會默認生成索引,所以表默認是有索引的也就是主鍵索引.我們先來看一個對于主鍵索引的樣例:

create table if not exists user (id int primary key, --一定要添加主鍵哦,只有這樣才會默認生成主鍵索引age int not null,name varchar(16) not null
);
mysql> show create table user \G
*************************** 1. row ***************************
Table: user
Create Table: CREATE TABLE `user` (`id` int(11) NOT NULL,`age` int(11) NOT NULL,`name` varchar(16) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 --默認就是InnoDB存儲引擎
1 row in set (0.00 sec)

然后我們插入一組無序的數據:

--插入多條記錄,注意,我們并沒有按照主鍵的大小順序插入
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)

查看結果的時候卻發現他們自動就是有序的了:

mysql> select * from user; --發現竟然默認是有序的!是誰干的呢?排序有什么好處呢?
+----+-----+-----------+
| id | age | name |
+----+-----+-----------+
| 1 | 56 | 歐陽鋒 |
| 2 | 26 | 黃蓉 |
| 3 | 18 | 楊過 |
| 4 | 16 | 小龍女 |
| 5 | 36 | 郭靖 |
+----+-----+-----------+
5 rows in set (0.00 sec)

理解單張page?

ok在此中斷一下,我們上面說了mysql與磁盤的基本交互單位為page,他是基于局部性原理這么做的.mysql中有許多表,而要管理好這些文件就需要先描述再組織,我們目前可以簡單理解成一個個獨立文件是有一個或者多個Page構成的。

????????不同的 Page ,在 MySQL 中,都是 16KB ,使用 prev 和 next 構成雙向鏈表.因為有主鍵的問題, MySQL 會默認按照主鍵給我們的數據進行排序,從上面的Page內數據記錄可以看出,數據是有序且彼此關聯的。
為什么數據庫在插入數據時要對其進行排序呢?我們按正常順序插入數據不是也挺好的嗎?插入數據時排序的目的,就是優化查詢的效率。頁內部存放數據的模塊,實質上也是一個鏈表的結構,鏈表的特點也就是增刪快,查詢修改慢,所以優化查詢的效率是必須的。也正是因為有序,在查找的時候,從頭到后都是有效查找,沒有任何一個查找是浪費的,而且,如果運氣好,是可以提前結束查找過程的。

理解多張page

????????通過上面的分析,我們知道,上面頁模式中,只有一個功能,就是整頁的數據加載到內存中,以減少硬盤IO次數,從而提高性能。但是,我們也可以看到,現在的頁模式內部,實際上是采用了鏈表的結構,前一條數據指向后一條數據,本質上還是通過數據的逐條比較來取出特定的數據

????????如果有1千萬條數據,一定需要多個Page來保存1千萬條數據,多個Page彼此使用雙鏈表鏈接起來,而且每個Page內部的數據也是基于鏈表的。那么,查找特定一條記錄,也一定是線性查找。這效率也太低了。

頁目錄?

? ? ? ? 我們平常看書的時候,在起始頁面總有幾頁是目錄.有了目錄,我們便可以通過目錄快速找到目標頁.而表數據查詢本質上是一個篩選淘汰的過程,其效率取決于每次查詢能夠排除的無效數據量。那么我們可不可以在mysql中也為每張表內部添加上目錄結構,mysql就是這樣做的(我們這里說的是innodb引擎):

? ? ? ? 誠然,單張page在添加了目錄之后,雖然損耗了一定的空間,但是極大的提高了在單張頁中的查詢效率.可是,多張page呢?軟件設計中存在一個悖論:遇到無法解決的問題時,我們總是傾向于添加中間層來解決。若問題仍未解決,就繼續疊加中間層。所以,我們可以在所有page的上一層,添加一層頁目錄,像多叉樹那樣:

但是如果多添加一層還不夠呢?那就再加一層!:

? ? ? ? 而多出來的所有層所有的page中,不存儲數據(innodb),只存儲目錄.這其實也就是對高階數據結構的使用-B+樹.隨便找一個id=?我們發現,現在查找的Page數一定減少了,也就意味著IO次數減少了,那么效率也就提高了。
所以我們可以總結一下,在innodb引擎中:

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

?InnoDB 在建立索引結構來管理數據的時候,其他數據結構為何不行?

  • 鏈表當然是不行的,我們上面也說過.
  • 二叉搜索樹?退化問題,可能退化成為線性結構
  • AVL &&紅黑樹?雖然是平衡或者近似平衡,但是畢竟是二叉結構,相比較多階B+,意味著樹整體過高,大家都是自頂向下找,層高越低,意味著系統與硬盤更少的IO Page交互。雖然你很秀,但是有更秀的。
  • Hash?官方的索引實現方式中, MySQL 是支持HASH的,不過 InnoDB 和 MyISAM 并不支持.Hash跟進其算法特征,決定了雖然有時候也很快(O(1)),不過,在面對范圍查找就明顯不行,另外還有其他差別,有興趣可以查一下。
  • B樹?最值得比較的是 InnoDB 為何不用B樹作為底層索引

B+ vs B

B樹:?

B+樹:

?

  • B樹節點,既有數據,又有Page指針,而B+,只有葉子節點有數據,其他目錄頁,只有鍵值Page指針
  • B+葉子節點,全部相連,而B沒有(利好范圍查找).

所以我們就可以理解為什么選擇B+樹了:

  • 節點不存儲data,這樣一個節點就可以存儲更多的key。可以使得樹更矮,所以IO操作次數更少。
  • 葉子節點相連,更便于進行范圍查找

Innodb和MyIsam都使用的是B+樹,那么他們有什么區別呢?

聚簇索引(Innodb)與非聚簇索引(MyISAM)

MyISAM 引擎同樣使用B+樹作為索引結果,葉節點的data域存放的是數據記錄的地址。下圖為 MyISAM表的主索引, Col1 為主鍵:

其中, MyISAM 最大的特點是,將索引Page和數據Page分離,也就是葉子節點沒有數據,只有對應數據的地址。相較于 InnoDB 索引, InnoDB 是將索引和數據放在一起的

--終端A
mysql> create database myisam_test; --創建數據庫
Query OK, 1 row affected (0.00 sec)
mysql> use myisam_test;
Database changed
mysql> create table mtest(
-> id int primary key,
-> name varchar(11) not null
-> )engine=MyISAM; --使用engine=MyISAM
Query OK, 0 rows affected (0.01 sec)
--終端B
[root@VM-0-3-centos mysql]# ls myisam_test/ -al --mysql數據目錄下
total 28
drwxr-x--- 2 mysql mysql 4096 Jun 13 13:33 .
drwxr-x--x 13 mysql mysql 4096 Jun 13 13:32 ..
-rw-r----- 1 mysql mysql 61 Jun 13 13:32 db.opt
-rw-r----- 1 mysql mysql 8586 Jun 13 13:33 mtest.frm --表結構數據
-rw-r----- 1 mysql mysql 0 Jun 13 13:33 mtest.MYD --該表對應的數據,當前沒有數
據,所以是0
-rw-r----- 1 mysql mysql 1024 Jun 13 13:33 mtest.MYI --該表對應的主鍵索引數據
--終端A
mysql> create database innodb_test; --創建數據庫
Query OK, 1 row affected (0.00 sec)
mysql> use innodb_test;
Database changed
mysql> create table itest(
-> id int primary key,
-> name varchar(11) not null
-> )engine=InnoDB; --使用engine=InnoDB
Query OK, 0 rows affected (0.02 sec)
--終端B
[root@VM-0-3-centos mysql]# ls innodb_test/ -al
total 120
drwxr-x--- 2 mysql mysql 4096 Jun 13 13:39 .
drwxr-x--x 14 mysql mysql 4096 Jun 13 13:38 ..
-rw-r----- 1 mysql mysql 61 Jun 13 13:38 db.opt
-rw-r----- 1 mysql mysql 8586 Jun 13 13:39 itest.frm --表結構數據
-rw-r----- 1 mysql mysql 98304 Jun 13 13:39 itest.ibd --該表對應的主鍵索引和用戶
數據,雖然現在一行數據沒有,但是該表并不為0,因為有主鍵索引數據

????????當然, MySQL 除了默認會建立主鍵索引外,我們用戶也有可能建立按照其他列信息建立的索引,一般這種索引可以叫做輔助(普通)索引。對于 MyISAM ,建立輔助(普通)索引和主鍵索引沒有差別,無非就是主鍵不能重復,而非主鍵可重復.

同樣, InnoDB 除了主鍵索引,用戶也會建立輔助(普通)索引,我們以上表中的 Col3 建立對應的輔助索引如下圖:

可以看到, InnoDB 的非主鍵索引中葉子節點并沒有數據,而只有對應記錄的key值。所以通過輔助(普通)索引,找到目標記錄,需要兩遍索引:首先檢索輔助索引獲得主鍵,然后用主鍵到主索引中檢索獲得記錄。這種過程,就叫做回表查詢.

1.3索引的操作

我們這里只介紹下主鍵,唯一鍵與普通索引.其他的索引諸如全文索引讀者可自行進行了解.?

1.3.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

1.3.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,等價于主鍵索引

1.3.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);

普通索引的特點:

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

?

?

?



?

?

?

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

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

相關文章

stm32f4 dma的一些問題

文章目錄前言一、使用開發板燒錄dma代碼不生效問題二、一個工程同時使用uart2、uart3借助dma來傳遞1.并行。2.DMA "同時工作"的本質3.總線訪問的具體含義4.實際效果5.最佳實踐5.1 總線傳輸機制:6.DMA傳輸中斷的問題總結前言 記錄一些使用stm32f4 dma過程…

登錄功能實現深度解析:從會話管理到安全校驗全流程指南

登錄功能實現深度解析:從會話管理到安全校驗全流程指南大家好,我是凱哥Java本文標簽:登錄驗證流程、過濾器與攔截器、安全防護措施簡介本文深入探討了從登錄功能實現到會話管理和安全校驗的全流程,包括參數校驗、身份驗證、令牌生…

2023 年 5 月青少年軟編等考 C 語言六級真題解析

目錄 T1. 字符串插入 思路分析 T2. 機器翻譯 思路分析 T3. 棧基本操作 思路分析 T4. 雙端隊列 思路分析 T1. 字符串插入 題目鏈接:SOJ D1138 有兩個字符串 s t r str str 和 s u b s t r substr substr, s t r str str 的字符個數不超過 10 10 10, s u b s t r substr …

Redux架構解析:狀態管理的核心原理

Redux 作為 JavaScript 應用的狀態管理庫,其技術架構與核心原理圍繞??可預測的狀態管理??設計,通過嚴格的單向數據流和函數式編程理念實現復雜應用的狀態控制。以下從設計理念、核心架構、工作流程、源碼實現等角度進行系統性剖析:一、設…

linux制作鏡像、壓縮鏡像、燒錄的方法

最近在玩香橙派的時候,需要搞多個板子,一個一個配環境也太麻煩了吧......于是通過搜索,發現可以把linux設備(比如香橙派,樹莓派等等)制作為鏡像,然后像燒錄官方鏡像一樣燒進新的sd卡&#xff0c…

機械材料計算軟件,快速核算重量

軟件介紹 今天為大家推薦一款專為機械行業設計人員打造的金屬材料重量計算軟件,幫助工程師快速完成材料重量核算。 軟件特點 這款綠色版計算工具體積小巧,不足100KB,無需安裝即可直接運行,不占用系統資源,特別適…

Chrome更新后,擴展不能用問題

Chrome更新后,擴展不能用問題 此擴展程序不再受支持,因此已停用 在 Windows 10/11 搜索框中輸入 regedit 打開 注冊表編輯器 在注冊表編輯器中打開:HKEYLOCALMACHINE\SOFTWARE\Policies\ 右鍵單擊 Policies 新建項 命名為 Google 右鍵單…

【Python】通過cmd的shell命令獲取局域網內所有IP、MAC地址,通過主機名獲取IP

【Python】通過cmd的shell命令獲取局域網內所有IP、MAC地址,通過主機名獲取IP 更新以gitee為準: gitee 文章目錄cmd命令獲取IPping主機名獲取IP的主機名socket獲取當前網關運行效果附錄:列表的賦值類型和py打包列表賦值BUG復現代碼改進優化總…

sky-take-out項目Mybatis的使用

分頁查詢public PageResult pageQuery(CategoryPageQueryDTO categoryPageQueryDTO) {PageHelper.startPage(categoryPageQueryDTO.getPage(),categoryPageQueryDTO.getPageSize());//下一條sql進行分頁&#xff0c;自動加入limit關鍵字分頁Page<Category> page categor…

H3CNE小小綜合實驗

實驗拓撲圖實驗需求 按照圖示配置IP地址在SW1和SW2之間配置鏈路聚合增加鏈路帶寬&#xff0c;提高可靠性PC5和PC6屬于VLAN10&#xff0c; PC7和PC8屬于VLAN20SW1和SW2屬于二層交換機&#xff0c;SW3為三層交換機&#xff08;VLAN100用于對接R4&#xff09;,在交換機之間相連的鏈…

IP協議深入理解

一、什么是ip協議?1.1、ip協議是網絡層協議&#xff0c;ip協議的本質工作是提供一種能力&#xff0c;把數據可靠的跨網絡從主機A送到主機B&#xff1b;1.2、什么是ip&#xff1f;ip目標網絡目標主機&#xff1b;畫圖圖解:如圖&#xff0c;當主機A想要把數據發給主機B時&#x…

接收表單數據:serialize()函數解析

一、form-serialize作用與引入 作用&#xff1a; form-serialize可以快速收集表單數據&#xff0c;按照使用者意愿轉化為對象或字符串輸出&#xff0c;以便于提交至服務器。 引入&#xff1a; form-serialize不是瀏覽器自帶的JS方法&#xff0c;而是第三方工具庫。可以直接通過…

vc配置使用預編譯

預編譯原理 stdafx.h中加入系統文件&#xff0c;減少cpp中對這些文件的解析&#xff0c;提高速度 stdafx.h 會把編譯的文件生成pch&#xff0c;后續解析頭文件直接調用pch里面的數據 配置 新建stdafx.h和stdafx.cpp文件 配置stdafx.cpp文件為/Yc 創建預編譯文件整個項目設置/Yc…

反射機制的登錄系統

一、實體層&#xff08;po層&#xff09; //UserInfo package com.hugeyurt.po;import java.sql.ResultSet; import java.sql.SQLException;public class UserInfo {private String userID;private String name;private int count;private Long errorTime;private String pwd;p…

裝飾器模式及優化

裝飾器模式&#xff08;Decorator Pattern&#xff09;是一種結構型設計模式&#xff0c;它允許向一個現有的對象添加新的功能&#xff0c;同時又不改變其結構。這種模式創建了一個裝飾器類&#xff0c;用來包裝原有的類&#xff0c;并在保持類方法簽名完整性的前提下&#xff…

共指消解技術全解析:從語言學規則到深度學習(附論文精讀)

精讀威斯康星大學綜述《Coreference Resolution: A Survey》&#xff0c;揭秘NLP中"實體鏈接"的核心技術一、什么是共指消解&#xff1f;為什么它是NLP的基石&#xff1f;共指消解(Coreference Resolution) 旨在識別文本中指向同一實體的不同表述。例如&#xff1a;t…

git配置git commit -m “fix 11,22: 修改bugid為11,22“

文章目錄前言一、報錯提示二、實現1.commitlint.config.js規范配置2. **修改正則表達式**&#xff1a;3. **移除 scope-case 規則**&#xff1a;4. **增強自定義規則邏輯**&#xff1a;測試結果&#xff1a;正則表達式詳解&#xff1a;前言 提示&#xff1a;正常的配置git規范…

nastools繼任者?極空間部署影視自動化訂閱系統『MediaMaster』

nastools繼任者&#xff1f;極空間部署影視自動化訂閱系統『MediaMaster』 哈嘍小伙伴們好&#xff0c;我是Stark-C~ 對于我們NAS玩家來說&#xff0c;觀影總是大家繞不開的一個執念&#xff0c;并且為觀影的折騰大家也都是樂此不疲~ 曾經有一個非常絕絕子的觀影神器擺在我們…

題解:CF1690G Count the Trains

思路&#xff1a; 首先我們可以理清一下各種情況&#xff1a;1&#xff09;m可能為02&#xff09;一次操作時&#xff0c;只需要考慮每節火車的車頭。3&#xff09;當一節火車的速度降低時&#xff0c;只會影響它及它后面的車廂當m0時&#xff0c;我們可以記錄上一節車頭的速度…

CCF編程能力等級認證GESP—C++3級—20250628

CCF編程能力等級認證GESP—C3級—20250628單選題&#xff08;每題 2 分&#xff0c;共 30 分&#xff09;判斷題&#xff08;每題 2 分&#xff0c;共 20 分&#xff09;編程題 (每題 25 分&#xff0c;共 50 分)奇偶校驗分糖果單選題&#xff08;每題 2 分&#xff0c;共 30 分…