【MySQL進階】索引使用

一、索引使用

1.驗證索引效率

tb_sku?這張表中準備了?1000w?的記錄。

我用夸克網盤分享了「1000w的模擬數據」鏈接:https://pan.quark.cn/s/15cf665202b2

?這張表中id為主鍵,有主鍵索引,而其他字段是沒有建立索引的。 我們先來查詢其中的一條記錄,看看里面的字段情況,執行如下SQL

select * from tb_sku where id = 1\G;

可以看到即使有1000w的數據,根據id進行數據查詢,性能依然很快,因為主鍵id是有索引的。 那么接下來,我們再來根據 sn 字段進行查詢,執行如下SQL

SELECT * FROM tb_sku WHERE sn = '100000003145001';

我們可以看到根據 sn 字段進行查詢,查詢返回了一條數據,結果耗時 20.78sec ,就是因為 sn 沒有索引,而造成查詢效率很低。
那么我們可以針對于 sn 字段,建立一個索引,建立了索引之后,我們再次根據 sn 進行查詢,再來看一下查詢耗時情況。

創建索引:

create index idx_sku_sn on tb_sku(sn) ;

?然后再次執行相同的SQL語句,再次查看SQL的耗時。

SELECT * FROM tb_sku WHERE sn = '100000003145001';

我們明顯會看到, sn 字段建立了索引之后,查詢性能大大提升。建立索引前后,查詢耗時都不是一個數量級的。

2.最左前綴法則

如果索引了多列(聯合索引),要遵守最左前綴法則。最左前綴法則指的是查詢從索引的最左列開始,并且不跳過索引中的列。如果跳躍某一列,索引將會部分失效( 后面的字段索引失效 )
tb_user 表為例,我們先來查看一下之前 tb_user 表所創建的索引。

在 tb_user 表中,有一個聯合索引,這個聯合索引涉及到三個字段,順序分別為:profession, age,status

對于最左前綴法則指的是,查詢時,最左變的列,也就是profession必須存在,否則索引全部失效。 而且中間不能跳過某一列,否則該列后面的字段索引將失效。 接下來,我們來演示幾組案例,看一下具體的執行計劃:

explain select * from tb_user where profession = '軟件工程' and age = 31 and status= '0';

explain select * from tb_user where profession = '軟件工程' and age = 31;

explain select * from tb_user where profession = '軟件工程';

?以上的這三組測試中,我們發現只要聯合索引最左邊的字段 profession存在,索引就會生效,只不過索引的長度不同。 而且由以上三組測試,我們也可以推測出profession字段索引長度為47age字段索引長度為2status字段索引長度為5

explain select * from tb_user where age = 31 and status = '0';

explain select * from tb_user where status = '0';

?而通過上面的這兩組測試,我們也可以看到索引并未生效,原因是因為不滿足最左前綴法則,聯合索引最左邊的列profession不存在。

explain select * from tb_user where profession = '軟件工程' and status = '0';

上述的SQL查詢時,存在profession字段,最左邊的列是存在的,索引滿足最左前綴法則的基本條 件。但是查詢時,跳過了age這個列,所以后面的列索引是不會使用的,也就是索引部分生效,所以索引的長度就是47

思考題:

當執行 SQL 語句 : explain select * from tb_user where age = 31 and status = '0' and profession = '軟件工程 ' ; 時,是否滿足最左前綴法則,走不走
上述的聯合索引,索引長度?
可以看到,是完全滿足最左前綴法則的,索引長度 54 ,聯合索引是生效的。
注意 : 最左前綴法則中指的最左邊的列,是指在查詢時,聯合索引的最左邊的字段 ( 即是
第一個字段 ) 必須存在,與我們編寫 SQL 時,條件編寫的先后順序無關。

3.范圍查詢

聯合索引中,出現范圍查詢(>,<),范圍查詢右側的列索引失效。

explain select * from tb_user where profession = '軟件工程' and age > 30 and status
= '0';

當范圍查詢使用 > < 時,走聯合索引了,但是索引的長度為 49 ,就說明范圍查詢右邊的 status
段是沒有走索引的。
explain select * from tb_user where profession = '軟件工程' and age >= 30 and status = '0';

當范圍查詢使用 >= <= 時,走聯合索引了,但是索引的長度為 54 ,就說明所有的字段都是走索引的。
所以,在業務允許的情況下,盡可能的使用類似于 >= <= 這類的范圍查詢,而避免使用 > <

4.索引失效情況

?1.索引列運算

不要在索引列上進行運算操作, 索引將失效。

tb_user 表中,除了前面介紹的聯合索引之外,還有一個索引,是 phone 字段的單列索引。

A. 當根據phone字段進行等值匹配查詢時, 索引生效。?

explain select * from tb_user where phone = '17799990015';

?B. 當根據phone字段進行函數運算操作之后,索引失效。

explain select * from tb_user where substring(phone,10,2) = '15';

2.字符串不加引號 ?
字符串類型字段使用時,不加引號,索引將失效。
接下來,我們通過兩組示例,來看看對于字符串類型的字段,加單引號與不加單引號的區別:
explain select * from tb_user where profession = '軟件工程' and age = 31 and status
= '0';
explain select * from tb_user where profession = '軟件工程' and age = 31 and status
= 0;

explain select * from tb_user where phone = '17799990015';
explain select * from tb_user where phone = 17799990015;

?

經過上面兩組示例,我們會明顯的發現,如果字符串不加單引號,對于查詢結果,沒什么影響,但是數據庫存在隱式類型轉換,索引將失效。
3.模糊查詢
如果僅僅是尾部模糊匹配,索引不會失效。如果是頭部模糊匹配,索引失效。
接下來,我們來看一下這三條 SQL 語句的執行效果,查看一下其執行計劃:
由于下面查詢語句中,都是根據 profession 字段查詢,符合最左前綴法則,聯合索引是可以生效的,我們主要看一下,模糊查詢時,% 加在關鍵字之前,和加在關鍵字之后的影響。
explain select * from tb_user where profession like '軟件%';
explain select * from tb_user where profession like '%工程';
explain select * from tb_user where profession like '%工%';

?

?經過上述的測試,我們發現,在like模糊查詢中,在關鍵字后面加%,索引可以生效。而如果在關鍵字前面加了%,索引將會失效。

4.or連接條件

or 分割開的條件, 如果 or 前的條件中的列有索引,而后面的列中沒有索引,那么涉及的索引都不會被用到。
explain select * from tb_user where id = 10 or age = 23;
explain select * from tb_user where phone = '17799990017' or age = 23;

由于age沒有索引,所以即使idphone有索引,索引也會失效。所以需要針對于age也要建立索引。

然后,我們可以對 age 字段建立索引。
create index idx_user_age on tb_user(age);
建立了索引之后,我們再次執行上述的 SQL 語句,看看前后執行計劃的變化。

最終,我們發現,當or連接的條件,左右兩側字段都有索引時,索引才會生效。

5.數據分布影響

如果MySQL評估使用索引比全表更慢,則不使用索引。

explain select * from tb_user where phone >= '17799990005';
explain select * from tb_user where phone >= '17799990015';

經過測試我們發現,相同的SQL語句,只是傳入的字段值不同,最終的執行計劃也完全不一樣,這是為什么呢?

就是因為 MySQL 在查詢時,會評估使用索引的效率與走全表掃描的效率,如果走全表掃描更快,則放棄索引,走全表掃描。 因為索引是用來索引少量數據的,如果通過索引查詢返回大批量的數據,則還不如走全表掃描來的快,此時索引就會失效。

5.SQL提示

目前tb_user表的數據情況如下:

索引情況如下:?

把上述的 idx_user_age, idx_email 這兩個之前測試使用過的索引直接刪除。 ?

A. 執行SQL :
explain select * from tb_user where profession = '軟件工程';

查詢走了聯合索引。 ?

B. 執行SQL,創建profession的單列索引:
create index idx_user_pro on tb_user(profession);
C. 創建單列索引后,再次執行A中的SQL語句,查看執行計劃,看看到底走哪個索引。

測試結果,我們可以看到,possible_keys中 idx_user_pro_age_sta,idx_user_pro 這兩個索引都可能用到,最終MySQL選擇了idx_user_pro_age_sta索引。這是MySQL自動選擇的結果。

那么,我們能不能在查詢的時候,自己來指定使用哪個索引呢?

答案是肯定的,此時就可以借助于 MySQL的SQL提示來完成。 接下來,介紹一下SQL提示。

SQL提示,是優化數據庫的一個重要手段,簡單來說,就是在SQL語句中加入一些人為的提示來達到優化操作的目的。
1). use index : 建議 MySQL使用哪一個索引完成此次查詢(僅僅是建議,mysql內部還會再次進
行評估)。
explain select * from tb_user use index(idx_user_pro) where profession = '軟件工
程';

2). ignore index : 忽略指定的索引。
explain select * from tb_user ignore index(idx_user_pro) where profession = '軟件工
程';

3). force index : 強制使用索引。 ?

explain select * from tb_user force index(idx_user_pro) where profession = '軟件工
程';

6.覆蓋索引

盡量使用覆蓋索引,減少select *。 那么什么是覆蓋索引呢? 覆蓋索引是指 查詢使用了索引,并 且需要返回的列,在該索引中已經全部能夠找到 。

接下來,我們來看一組SQL的執行計劃,看看執行計劃的差別,然后再來具體做一個解析。

explain select id, profession from tb_user where profession = '軟件工程' and age =
31 and status = '0' ;
explain select id,profession,age, status from tb_user where profession = '軟件工程'
and age = 31 and status = '0' ;
explain select id,profession,age, status, name from tb_user where profession = '軟
件工程' and age = 31 and status = '0' ;
explain select * from tb_user where profession = '軟件工程' and age = 31 and status
= '0';

?

從上述的執行計劃我們可以看到,這四條 SQL 語句的執行計劃前面所有的指標都是一樣的,看不出來差異。但是此時,我們主要關注的是后面的Extra ,前面兩個 SQL 的結果為 Using where; Using
Index ; 而后面兩條 SQL 的結果為 : Using index condition
因為,在 tb_user 表中有一個聯合索引 idx_user_pro_age_sta ,該索引關聯了三個字段 profession、 age status ,而這個索引也是一個二級索引,所以葉子節點下面掛的是這一行的主
id 。 所以當我們查詢返回的數據在 id profession age status 之中,則直接走二級索引
直接返回數據了。 如果超出這個范圍,就需要拿到主鍵 id,再去掃描聚集索引,再獲取額外的數據
了,這個過程就是回表。 而我們如果一直使用 select * 查詢返回所有字段值,很容易就會造成回表
查詢(除非是根據主鍵查詢,此時只會掃描聚集索引)。
為了大家更清楚的理解,什么是覆蓋索引,什么是回表查詢,我們一起再來看下面的這組 SQL 的執行過程。
A. 表結構及索引示意圖:

?id是主鍵,是一個聚集索引。 name字段建立了普通索引,是一個二級索引(輔助索引)。

?B. 執行SQL :

select * from tb_user where id = 2;

根據id查詢,直接走聚集索引查詢,一次索引掃描,直接返回數據,性能高。 ?

C. 執行SQL

selet id,name from tb_user where name = 'Arm';

?雖然是根據name字段查詢,查詢二級索引,但是由于查詢返回在字段為 idname,在name的二級索引中,這兩個值都是可以直接獲取到的,因為覆蓋索引,所以不需要回表查詢,性能高。

D. 執行SQL
selet id,name,gender from tb_user where name = 'Arm';

由于在 name 的二級索引中,不包含 gender ,所以,需要兩次索引掃描,也就是需要回表查詢,性能相對較差一點。

思考題:
一張表 , 有四個字段 (id, username, password, status), 由于數據量大 , 需要對以下SQL 語句進行優化 , 該如何進行才是最優方案 :
select id,username,password from tb_user where username = 'itcast';

答案 : 針對于 username, password 建立聯合索引 , sql為:
create index idx_user_name_pass on tb_user(username,password);
這樣可以避免上述的 SQL 語句,在查詢的過程中,出現回表查詢。

7.前綴索引

當字段類型為字符串(varchartextlongtext等)時,有時候需要索引很長的字符串,這會讓索引變得很大,查詢時,浪費大量的磁盤IO, 影響查詢效率。此時可以只將字符串的一部分前綴,建立索引,這樣可以大大節約索引空間,從而提高索引效率。

1). 語法

create index idx_xxxx on table_name(column(n)) ;
示例 :
tb_user 表的 email 字段,建立長度為 5 的前綴索引。
create index idx_email_5 on tb_user(email(5));

2). 前綴長度
可以根據索引的選擇性來決定,而選擇性是指不重復的索引值(基數)和數據表的記錄總數的比值, 索引選擇性越高則查詢效率越高, 唯一索引的選擇性是1 ,這是最好的索引選擇性,性能也是最好的。
select count(distinct email) / count(*) from tb_user ;
select count(distinct substring(email,1,5)) / count(*) from tb_user ;

3). 前綴索引的查詢流程

8.單列索引與聯合索引

單列索引:即一個索引只包含單個列。
聯合索引:即一個索引包含了多個列。
我們先來看看 tb_user 表中目前的索引情況 :

在查詢出來的索引中,既有單列索引,又有聯合索引。 ?

接下來,我們來執行一條 SQL 語句,看看其執行計劃:
explain select id,phone,name from tb_user where phone='17799990010' AND name='韓信';

通過上述執行計劃我們可以看出來,在 and 連接的兩個字段 phone name 上都是有單列索引的,但是最終mysql 只會選擇一個索引,也就是說,只能走一個字段的索引,此時是會回表查詢的。
緊接著,我們再來創建一個 phone name 字段的聯合索引來查詢一下執行計劃。

create unique index idx_user_phone_name on tb_user(phone,name);

此時,查詢時,就走了聯合索引,而在聯合索引中包含 phone name 的信息,在葉子節點下掛的是對應的主鍵id ,所以查詢是無需回表查詢的。
在業務場景中,如果存在多個查詢條件,考慮針對于查詢字段建立索引時,建議建立聯合索引,而非單列索引。

?

二、索引設計原則

1). 針對于數據量較大,且查詢比較頻繁的表建立索引。
2). 針對于常作為查詢條件( where )、排序( order by )、分組( group by )操作的字段建立索
引。
3). 盡量選擇區分度高的列作為索引,盡量建立唯一索引,區分度越高,使用索引的效率越高。
4). 如果是字符串類型的字段,字段的長度較長,可以針對于字段的特點,建立前綴索引。
5). 盡量使用聯合索引,減少單列索引,查詢時,聯合索引很多時候可以覆蓋索引,節省存儲空間, 避免回表,提高查詢效率。
6). 要控制索引的數量,索引并不是多多益善,索引越多,維護索引結構的代價也就越大,會影響增刪改的效率。
7). 如果索引列不能存儲 NULL 值,請在創建表時使用 NOT NULL 約束它。當優化器知道每列是否包含NULL值時,它可以更好地確定哪個索引最有效地用于查詢。

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

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

相關文章

JS基礎之原型原型鏈

JS基礎之原型&原型鏈 原型&原型鏈構造函數創建對象prototypeprotoconstructor實例與原型原型的原型原型鏈其他constructorproto繼承 原型&原型鏈 構造函數創建對象 我們先使用構造函數創建一個對象&#xff1a; function Person(){ } var person new Person();…

多窗口文件管理工具Q-Dir安裝以及使用教程

軟件介紹 Q-Dir 是一款功能強大的Windows資源管理器&#xff0c;可以非常方便的管理你的各種文件。Q-Dir有4 個窗口&#xff0c;特別適用于頻繁在各個目錄間跳躍復制粘貼的情況&#xff0c;每個窗口都可以方便的切換目錄&#xff0c;以不同顏色區分不同類型的文件&#xff0c;…

(企業項目)微服務項目解決跨域問題:

前后端分離項目中前端出現了跨域的問題 在網關模塊配置文件中添加 配置 application.properties # 允許請求來源&#xff08;老版本叫allowedOrigin&#xff09; spring.cloud.gateway.globalcors.cors-configurations.[/**].allowedOriginPatterns* # 允許攜帶的頭信息 spri…

idea__SpringBoot微服務06——靜態資源(新依賴),首頁和圖標定制

靜態資源 一、靜態資源二、首頁和圖標定制————————創作不易&#xff0c;如覺不錯&#xff0c;隨手點贊&#xff0c;關注&#xff0c;收藏(*&#xffe3;︶&#xffe3;)&#xff0c;謝謝~~ 新依賴&#xff1a;jquery的 <dependency><groupId>org.webjars&…

說說設計體系、風格指南和模式庫

目錄 一、定義 二、設計體系 2.1 Design system 2.2 風格指南 2.3 Component 三、樣式庫 一、定義 設計體系&#xff08;Design system&#xff09;&#xff1a;可共享的設計語言的基礎合集&#xff0c;包含了設計價值&#xff0c;語義&#xff0c;語法和上下文。 風格…

matplotlib 默認屬性和繪圖風格

matplotlib 默認屬性 一、繪圖風格1. 繪制疊加折線圖2. Solarize_Light23. _classic_test_patch4. _mpl-gallery5. _mpl-gallery-nogrid6. bmh7. classic8. fivethirtyeight9. ggplot10. grayscale11. seaborn12. seaborn-bright13. seaborn-colorblind14. seaborn-dark15. sea…

Chart 7 內存優化

文章目錄 前言7.1 Adreno GPU OpenCL內存7.1.1 內存聲明周期7.1.2 Loacl Memory7.1.3 Constant memory(常量內存)7.1.4 Private Memory7.1.5 Global Memory7.1.5.1 Buffer Object7.1.5.2 Image Object7.1.5.3 Image object vs. buffer object7.1.5.4 Use of both Image and buf…

C語言數據結構-雙向鏈表

文章目錄 1 雙向鏈表的結構2 雙向鏈表的實現2.1 定義雙向鏈表的數據結構2.2 打印鏈表2.3 初始化鏈表2.4 銷毀鏈表2.5 尾插,頭插2.6 尾刪,頭刪2.7 根據頭次出現數據找下標2.8 定點前插入2.9 刪除pos位置2.10 定點后插入 3 完整代碼3.1 List.h3.2 Lish.c3.3 test.c 1 雙向鏈表的結…

ajax中get和post的區別,datatype返回的數據類型有哪些?web開發中數據提交的幾種方式,有什么區別。百度使用哪種方式?

在Ajax中&#xff0c;GET和POST是兩種常見的HTTP請求方法。它們有以下區別&#xff1a; GET請求&#xff1a;使用GET請求時&#xff0c;參數數據會附加在URL的末尾&#xff0c;以查詢字符串的形式發送給服務器。GET請求是冪等的&#xff0c;也就是說多次發送相同的GET請求&…

鍵盤打字盲打練習系列之矯正坐姿——4

一.歡迎來到我的酒館 盲打&#xff0c;矯正坐姿&#xff01; 目錄 一.歡迎來到我的酒館二.繼續練習二.矯正坐姿1.鍵鼠快速選購指南2.椅子快速選購指南 三.改善坐姿建議 二.繼續練習 前面的章節&#xff0c;我們重點向大家介紹了主鍵盤區指法和鍵盤鍵位。經過一個系列的教程學習…

Mybatis環境搭建

1、開發環境 IDE&#xff1a;IntelliJ IDEA 2022.2.1 (Ultimate Edition) 構建工具&#xff1a;maven 3.6.1 MySQL版本&#xff1a;MySQL 5.7 MyBatis版本&#xff1a;MyBatis 3.5.14 2、工程創建 創建一個Maven工程giser-java-mybatis-demo 基礎依賴如下&#xff1a; &…

【Python】pip命令及使用

PIP命令 下面是一個整理成表格的pip命令及使用的示例&#xff1a; 命令使用示例說明pip install <package>pip install requests安裝名為"requests"的包pip uninstall <package>pip uninstall requests卸載名為"requests"的包pip listpip li…

用友U8 Cloud 多處反序列化RCE漏洞復現

0x01 產品簡介 用友U8 Cloud是用友推出的新一代云ERP,主要聚焦成長型、創新型企業,提供企業級云ERP整體解決方案。 0x02 漏洞概述 用友U8 Cloud存在多處(TableInputOperServlet、LoginServlet 、FileTransportServlet、CacheInvokeServlet、ActionHandlerServlet、Servle…

12.9每日一題(備戰藍橋杯循環結構)

12.9每日一題&#xff08;備戰藍橋杯循環結構&#xff09; 題目 2165: 求平均年齡題目描述輸入輸出樣例輸入樣例輸出來源/分類 題解 2165: 求平均年齡題目 2166: 均值題目描述輸入輸出樣例輸入樣例輸出來源/分類 題解 2166: 均值題目 2167: 求整數的和與均值題目描述輸入輸出樣…

GB/T 43212-2023 竹炭板檢測

竹炭塑復合板是指以竹炭粉為主要原料&#xff0c;與塑料及其他助劑復配混合&#xff0c;經熔融擠出或模壓成型等工藝制成的板材。 GB/T 43212-2023 竹炭板測試&#xff1a; 測試項目 測試方法 外觀 GB/T 43212 尺寸 GB/T 19367 含水率 GB/T 17657 密度 GB/T 17657 吸…

【rabbitMQ】springboot整合rabbitMQ模擬簡單收發消息

目錄 1.創建項目和模塊 2.添加rabbitMQ依賴 3.啟動rabbitMQ服務 4.引入rabbitMQ服務端信息 5.通過單元測試模擬業務發送消息 6. 接收消息 1.創建項目和模塊 2.添加rabbitMQ依賴 <!-- rabbitmq依賴--> <dependency> <groupId>org.sp…

JavaEE 09 鎖策略

1.鎖策略 1.1 樂觀鎖與悲觀鎖 其實前三個鎖是同一種鎖,只是站在不同的角度上去進行描述,此處的樂觀與悲觀其實是指在預測的角度上看會發生鎖競爭的概率大小,概率大的則是悲觀鎖,概率小的則是樂觀鎖 樂觀鎖在加鎖的時候就會做較少的事情,加鎖的速度較快,但是消耗的cpu資源等也會…

排序算法-選擇/堆排序(C語言)

1基本思想&#xff1a; 每一次從待排序的數據元素中選出最小&#xff08;或最大&#xff09;的一個元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的 數據元素排完 。 2 直接選擇排序: 在元素集合 array[i]--array[n-1] 中選擇關鍵碼最大 ( 小 ) 的數據元素…

PHP基礎 - 數組遍歷與排序

介紹 在PHP中,數組遍歷和排序是常見的操作,用于對數組中的元素進行訪問和排序 數組遍歷 1)數值數組的遍歷 使用 foreach 循環遍歷數組:foreach 循環是最常用的遍歷數組的方法,它可以遍歷索引數組和關聯數組。例如:$fruits = array("apple", "banana&q…

AG1KLPQ48 User Manual

1.&#xff09;軟件安裝&#xff1a; 解壓縮或執行安裝文件&#xff0c;安裝 Supra 軟件。執行文件為 bin 目錄中的 Supra.exe。 運行 Supra&#xff0c;選擇菜單 File -> Import license&#xff0c;選擇 license 文件并導入 License。 2.&#xff09;新建項目&#xff1a;…