Mysql進階篇(二)之索引

来源:https://www.cnblogs.com/yun3k/archive/2023/07/16/17558461.html
-Advertisement-
Play Games

# 一. 索引概述 ## 1. 介紹 **索引是幫助MySQL高效獲取數據的數據結構(有序)。在數據之外,資料庫系統還維護著滿足特定查找演算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找演算法,這種數據結構就是索引。** ![](https://tcs-de ...


一. 索引概述

1. 介紹

索引是幫助MySQL高效獲取數據的數據結構(有序)。在數據之外,資料庫系統還維護著滿足特定查找演算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找演算法,這種數據結構就是索引。

2. 演示

表結構及其數據如下:

假如我們要執行的SQL語句為:select * from user where age = 45;

(1). 無索引情況

在無索引情況下,就需要從第一行開始掃描,一直掃描到最後一行,我們稱之為 全表掃描,性能很低。

(2). 有索引情況

如果我們針對於這張表建立了索引,假設索引結構就是二叉樹,那麼也就意味著,會對age這個欄位建立一個二叉樹的索引結構。

此時我們在進行查詢時,只需要掃描三次就可以找到數據了,極大的提高了查詢的效率。

註:這裡我們只是假設索引的結構是二叉樹,介紹一下索引的大概原理,只是一個示意圖,並不是索引的真實結構,索引的真實結構,下麵會有介紹。

3. 特點

優勢 劣勢
提高數據檢索的效率,降低資料庫的IO成本 索引列也是要占用空間的
通過索引列對數據進行排序,降低數據排序的成本,降低CPU的消耗 索引大大提高了查詢效率,同時卻也降低了更新表的速度,如對錶進行INSERT、UPDATE、DELETE時,效率降低

二. 索引結構

1. 簡介

MySQL的索引是在存儲引擎層實現的,不同的存儲引擎有不同的索引結構,主要包含以下幾種:

索引結構 描述
B+Tree索引 最常見的索引類型,大部分引擎都支持B+樹索引
Hash索引 底層數據結構是用哈希表實現的,只有精確匹配索引列的查詢才有效,不支持範圍查詢
R-tree(空間索引) 空間索引是MyISAM引擎的一個特殊索引類型,主要用於地理空間數據類型,通常使用較少
Full-text(全文索引) 全文索引是一種通過建立倒排索引,快速匹配文檔的方式。類似於Lucene,Solr,ES

上述是MySQL中所支持的所有索引結構,接下來,我們再來看看不同的存儲引擎對於索引結構的支持情況。

索引 InnoDB MyISAM Memory
B+tree索引 支持 支持 支持
Hash索引 不支持 不支持 支持
R-tree索引 不支持 支持 不支持
Full-text索引 5.6版本之後支持 支持 不支持

2. 二叉樹

假如說MySQL的索引結構採用二叉樹的數據結構,比較理想的結構如下:

如果主鍵是順序插入的,則會形成一個單向鏈表,結構如下:

所以,如果選擇二叉樹作為索引結構,會存在以下缺點:

  • 順序插入時,會形成一個鏈表,查詢性能大大降低。

  • 大數據量情況下,層級較深,檢索速度慢

此時可能會想到,我們可以選擇紅黑樹,紅黑樹是一顆自平衡二叉樹,那這樣即使是順序插入數據,最終形成的數據結構也是一顆平衡的二叉樹,結構如下:

但是,即使如此,由於紅黑樹也是一顆二叉樹,所以也會存在一個缺點:

  • 大數據量情況下,層級較深,檢索速度慢。

所以,在MySQL的索引結構中,並沒有選擇二叉樹或者紅黑樹,而是選擇B+Tree,在詳解B+Tree之前,先來介紹一個B-Tree。

3. B-Tree

B-Tree,B樹是一種多叉路平衡查找樹,相對於二叉樹,B樹每個節點可以有多個分支,即多叉。

以一顆最大度數(max-degree)為5(5階)的b-tree為例,那這個B樹每個節點最多存儲4個key,5個指針:

註:樹的度數指的是一個節點的子節點格式

特點:

  • 5階的B樹,每一個節點最多存儲4個key,對應5個指針。

  • 一旦節點存儲的key數量達到5,就會裂變,中間元素向上分裂。

  • 在B樹中,非葉子節點和葉子節點都會存放數據。

4. B+Tree

B+Tree是B-Tree的變種,我們以一顆最大度數(max-degree)為4(4階)的b+tree為例,來看一下其結構示意圖:

我們可以看到,兩部分:

  • 綠色框框起來的部分,是索引部分,僅僅起到索引數據的作用,不存儲數據。

  • 紅色框框起來的部分,是數據存儲部分,在其葉子節點中要存儲具體的數據。

B+Tree和B-Tree相比,主要有以下三點區別:

  • 所有的數據都會出現在葉子節點。

  • 葉子節點形成一個單向鏈表。

  • 非葉子節點僅僅起到索引數據作用,具體的數據都是在葉子節點存放的。

上述我們所看到的結構是標準的B+Tree的數據結構,接下來,我們再來看看MySQL優化之後的B+Tree。

MySQL索引數據結構對經典的B+Tree進行了優化。在原B+Tree的基礎上,增加一個指向相鄰葉子節點的鏈表指針,就形成了帶有順序指針的B+Tree,提高區間訪問的性能,利於排序。

5. Hash

MySQL中除了支持B+Tree索引,還支持一種索引---Hash索引。

(1). 結構

哈希索引就是採用一定的hash演算法,將鍵值換算成新的hash值,映射到對應的槽位上,然後存儲在hash表中。

如果兩個(或多個)鍵值,映射到一個相同的槽位上,他們就產生了hash衝突(也稱為hash碰撞),可以通過鏈表來解決。

(2). 特點

  • Hash索引只能用於對等比較(=,in),不支持範圍查詢(between,>,<,...)

  • 無法利用索引完成排序操作

  • 查詢效率高,通常(不存在hash衝突的情況)只需要一次檢索就可以了,效率通常要高於B+Tree索引

(3). 存儲引擎支持

在MySQL中,支持hash索引的是Memory存儲引擎。而InnoDB中具有自適應hash功能,hash索引是InnoDB存儲引擎根據B+Tree索引在指定條件下自動構建的。

問題1:為什麼InnoDB存儲引擎選擇使用B+Tree索引結構?

  • 相對於二叉樹,層級更少,搜索效率高;

  • 對於B-tree,無論是葉子節點還是非葉子節點,都會保存數據,這樣導致一頁中存儲的鍵值減少,指針跟著減少,要同樣保存大量數據,只能增加樹的高度,導致性能降低;

  • 相對Hash索引,B+tree支持範圍匹配及排序操作。

三. 索引分類

在MySQL資料庫中,將索引的具體類型主要分為以下幾類:主鍵索引、唯一索引、常規索引、全文索引。

分類 含義 特點 關鍵字
主鍵索引 針對於表中主鍵創建的索引 預設自動創建,只能有一個 PRIMARY
唯一索引 避免同一個表中某數據列中的值重覆 可以有多個 UNIQUE
常規索引 快速定位特定數據 可以有多個
全文索引 全文索引查找的是文本中的關鍵詞,而不是比較索引中的值 可以有多個 FULLTEXT

1. 聚集索引&二級索引

在InnoDB存儲引擎中,根據索引的存儲形式,又可以分為以下兩種:

分類 含義 特點
聚集索引(Clustered Index) 將數據存儲與索引放到了一塊,索引結構的葉子節點保存了行數據 必須有,而且只有一個
二級索引(Secondary Index) 將數據與檢索分開存儲,索引結構的葉子節點關聯的是對應的主鍵 可以存在多個

聚集索引選取規則:

  • 如果存在主鍵,主鍵索引就是聚集索引。

  • 如果不存在主鍵,將使用第一個唯一(UNIQUE)索引作為聚集索引。

  • 如果表沒有主鍵,或沒有合適的唯一索引,則InnoDB會自動生成一個rowid作為隱藏的聚集索引。

聚集索引和二級索引的具體結構如下:

  • 聚集索引的葉子節點下掛的是這一行的數據。

  • 二級索引的葉子節點下掛的是該欄位值對應的主鍵值。

接下來,我們來分析一下,當我們執行如下的SQL語句時,具體的查找過程是什麼樣子的。

具體過程如下:

A. 由於是根據name欄位進行查詢,所以先根據name="Arm"到name欄位的二級索引中進行匹配查找。但是在二級索引中只能查找到Arm對應的主鍵值10.

B. 由於查詢返回的數據是*,所以此時,還需要根據主鍵值10到聚集索引中查找10對應的記錄,最終找到10對應的行row。

C. 最終拿到這一行的數據,直接返回即可。

回表查詢:這種先到二級索引中查找數據,找到主鍵值,然後再到聚集索引中根據主鍵值,獲取數據的方式,就稱之為回表查詢。

問題1:一下兩條SQL語句,哪個執行效率高?為什麼?

A. select * from user where id = 10;

B. select * from user where name = 'Arm';

備註:id為主鍵,name欄位創建的有索引;

解答:A語句的執行性能要高於B語句。

因為A語句直接走聚集索引,直接返回數據。而B語句需要先查詢name欄位的二級索引,然後再查詢聚集索引,也就是需要進行回表查詢。

問題2:InnoDB主鍵索引的B+tree高度為多高呢?

假設:

一行數據大小為1k,一頁中可以存儲16行。InnoDB的指針占用6個位元組的空間,主鍵即使為bigint,占用位元組數為8。

高度為2:

n * 8 + (n + 1) * 6 = 16 * 1024,算出n約為1170

1171 * 16 = 18736

也就是說,如果樹的高度為2,則可以存儲18000多條數據。

高度為3:

1171 * 1171 * 16 = 21939856

也就是說,如果樹的高度為3,則可以存儲2200w左右的記錄。

四. 索引語法

1. 創建索引

CREATE [UNIQUE | FULLTEXT] INDEX index_name ON table_name (index_col_name,...);

2. 查看索引

SHOW INDEX FROM table_name;

3. 刪除索引

DROP INDEX index_name ON table_name;

4. 案例演示

(1). 準備數據

create table tb_user( 
	id int primary key auto_increment comment '主鍵', 
	name varchar(50) not null comment '用戶名', 
	phone varchar(11) not null comment '手機號', 
	email varchar(100) comment '郵箱', 
	profession varchar(11) comment '專業', 
	age tinyint unsigned comment '年齡', 
	gender char(1) comment '性別 , 1: 男, 2: 女', 
	status char(1) comment '狀態', 
	createtime datetime comment '創建時間' 
) comment '系統用戶表';

INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('呂布', '17799990000', '[email protected]', '軟體工程', 23, '1', '6', '2001-02-02 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('曹操', '17799990001', '[email protected]', '通訊工程', 33, '1', '0', '2001-03-05 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('趙雲', '17799990002', '[email protected]', '英語', 34, '1', '2', '2002-03-02 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('孫悟空', '17799990003', '[email protected]', '工程造價', 54, '1', '0', '2001-07-02 00:00:00'); INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('花木蘭', '17799990004', '[email protected]', '軟體工程', 23, '2', '1', '2001-04-22 00:00:00'); INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('大喬', '17799990005', '[email protected]', '舞蹈', 22, '2', '0', '2001-02-07 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('露娜', '17799990006', '[email protected]', '應用數學', 24, '2', '0', '2001-02-08 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('程咬金', '17799990007', '[email protected]', '化工', 38, '1', '5', '2001-05-23 00:00:00'); INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('項羽', '17799990008', '[email protected]', '金屬材料', 43, '1', '0', '2001-09-18 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('白起', '17799990009', '[email protected]', '機械工程及其自動 化', 27, '1', '2', '2001-08-16 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('韓信', '17799990010', '[email protected]', '無機非金屬材料工 程', 27, '1', '0', '2001-06-12 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('荊軻', '17799990011', '[email protected]', '會計', 29, '1', '0', '2001-05-11 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('蘭陵王', '17799990012', '[email protected]', '工程造價', 44, '1', '1', '2001-04-09 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('狂鐵', '17799990013', '[email protected]', '應用數學', 43, '1', '2', '2001-04-10 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('貂蟬', '17799990014', '[email protected]', '軟體工程', 40, '2', '3', '2001-02-12 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('妲己', '17799990015', '[email protected]', '軟體工程', 31, '2', '0', '2001-01-30 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('羋月', '17799990016', '[email protected]', '工業經濟', 35, '2', '0', '2000-05-03 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('嬴政', '17799990017', '[email protected]', '化工', 38, '1', '1', '2001-08-08 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('狄仁傑', '17799990018', '[email protected]', '國際貿易', 30, '1', '0', '2007-03-12 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('安琪拉', '17799990019', '[email protected]', '城市規劃', 51, '2', '0', '2001-08-15 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('典韋', '17799990020', '[email protected]', '城市規劃', 52, '1', '2', '2000-04-12 00:00:00'); INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('廉頗', '17799990021', '[email protected]', '土木工程', 19, '1', '3', '2002-07-18 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('後羿', '17799990022', '[email protected]', '城市園林', 20, '1', '0', '2002-03-10 00:00:00'); 
INSERT INTO tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('姜子牙', '17799990023', '[email protected]', '工程造價', 29, '1', '4', '2003-05-26 00:00:00');

(2). name欄位為姓名欄位,該欄位的值可能會重覆,為該欄位創建索引。

CREATE INDEX idx_user_name ON tb_user(name);

(3). phone手機號欄位的值是非空且唯一的,為該欄位創建唯一索引。

CREATE UNIQUE INDEX idx_user_phone ON tb_user(phone);

(4). 為profession、age、status創建聯合索引。

CREATE INDEX idx_user_pro_age_sta ON tb_user(profession,age,status);

(5). 為email建立合適的索引來提升查詢效率。

CREATE INDEX idx_email ON tb_user(email);

五. SQL性能分析

1. SQL執行頻率

MySQL客戶端連接成功後,通過show [session | global] status 命令可以提供伺服器狀態信息。通過如下指令,可以查看當前資料庫的INSERT、UPDATE、DELETE、SELECT的訪問頻次:

-- session 是查看當前會話; 
-- global 是查詢全局數據; 

SHOW GLOBAL STATUS LIKE 'Com_______';

Com_delete:刪除次數

Com_insert:插入次數

Com_select:查詢次數

Com_update:更新次數

註:通過上述指令,我們可以查看到當前資料庫到底是以查詢為主,還是以增刪該為主,從而為資料庫優化提供參考依據。如果是以增刪改為主,我們可以考慮不對其進行索引的優化。如果是以查詢為主,那麼就要考慮對資料庫的索引進行優化了。

2. 慢查詢日誌

慢查詢日誌記錄了所有執行時間超過指定參數(long_query_time,單位:秒,預設10秒)的所有SQL語句的日誌。

MySQL的慢查詢日誌預設沒有開啟,我們可以查看一下系統變數slow_query_log。

show variables like 'slow_query_log';

如果要開啟慢查詢日誌,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:

# 開啟MySQL慢日誌查詢開關 
slow_query_log=1 

# 設置慢日誌的時間為2秒,SQL語句執行時間超過2秒,就會視為慢查詢,記錄慢查詢日誌 
long_query_time=2

配置完畢之後,通過以下指令重新啟動MySQL伺服器進行測試,查看慢日誌文件中記錄的信息 /var/lib/mysql/localhost-slow.log

systemctl restart mysqld

然後,再次查看開關情況,慢查詢日誌就已經打開了。

測試:

A. 執行如下SQL語句:

select * from tb_user; -- 這條SQL執行效率比較高, 執行耗時 0.00sec

select count(*) from tb_sku; -- 由於tb_sku表中, 預先存入了1000w的記錄, count一次,耗時 13.35sec

B. 檢查慢查詢日誌:

最終我們發現,在慢查詢日誌中,只會記錄執行時間超出我們預設時間(2s)的SQL,執行較快的SQL是不會記錄的。

那這樣,通過慢查詢日誌,就可以定位出執行效率比較低的SQL,從而有針對性的進行優化。

3. profile詳情

show profiles能夠在做SQL優化時幫助我們瞭解時間都耗費到哪裡去了。通過hava_profiling參數,能夠看到當前MySQL是否支持profile操作:

SELECT @@have_profiling;

SELECT @@profiling;

可以看到,當前MySQL是支持profile操作的,但是開關是關閉的。可以通過set語句在

session/global級別開啟profiling:

SET profiling = 1;

開關已經打開了,接下來,我們所執行的SQL語句,都會被MySQL記錄,並記錄執行時間消耗到哪兒去了。我們直接執行如下的SQL語句:

select * from tb_user; 

select * from tb_user where id = 1; 

select * from tb_user where name = '白起'; 

select count(*) from tb_sku;

執行一系列的業務SQL的操作,然後通過如下指令查看指令的執行耗時:

-- 查看每一條SQL的耗時基本情況 
show profiles; 

-- 查看指定query_id的SQL語句各個階段的耗時情況 
show profile for query query_id; 

-- 查看指定query_id的SQL語句CPU的使用情況 
show profile cpu for query query_id;

查看每一條SQL的耗時情況:

查看指定SQL各個階段的耗時情況:

4. explain

explain命令獲取MySQL如何執行SELECT語句的信息,包括在SELECT語句執行過程中表如何連接和連接的順序。

(1). 語法

-- 直接在select語句之前加上關鍵字 explain 
EXPLAIN SELECT 欄位列表 FROM 表名 WHERE 條件;

(2). explain執行計劃中各個欄位的含義

欄位 含義
id select查詢的序列號,表示查詢中執行select子句或者是操作表的順序(id相同,執行順序從上到下;id不同,值越大,越先執行)。
select_type 表示SELECT的類型,常見的取值有SIMPLE(簡單表,即不使用表連接或者子查詢)、PRIMARY(主查詢,即外層的查詢)、UNION(UNION中的第二個或者後面的查詢語句)、SUBQUERY(SELECT/WHERE之後包含了子查詢)等
type 表示連接類型,性能由好到差的連接類型為NULL、system、const、eq_ref、ref、range、index、all。
possible_key 顯示可能應用在這張表上的索引,一個或多個。
key 實際使用的索引,如果為NULL,則沒有使用索引。
key_len 表示索引中使用的位元組數,該值為索引欄位最大可能長度,並非實際使用長度,在不損失精確性的前提下,長度越短越好。
rows MySQL認為必須要執行查詢的行數,在innodb引擎的表中,是一個估計值,可能並不總是準確的。
filtered 表示返回結果的行數占需讀取行數的百分比,filtered的值越大越好。

六. 索引使用

1. 驗證索引效率

我們先通過一個簡單的案例,來驗證一下索引,看看是否能夠通過索引來提升數據查詢性能。在演示的時候,我們還是使用之前準備得一張表tb_sku,在這種表中準備了1000w的記錄。

這種表中id為主鍵,有主鍵索引,而其他欄位是沒有建立索引的。我們先來查詢其中的一條記錄,看看裡面的欄位情況,執行如下SQL:

select * from tb_sku where id = 1\G;

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

SELECT * FROM tb_sku WHERE sn = '100000003145001';

我們可以看到根據sn欄位進行查詢,查詢返回了一條數據,結果耗時20.78 sec,就是因為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欄位索引長度為47、age欄位索引長度為2、status欄位索引長度為5。

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

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

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

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

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

問題1:當執行SQL語句:explain select * from tb_user where age = 31 and status = '0' and profeddion = '軟體工程';時,是否滿足最左首碼法則,走不走上述的聯合索引,索引長度是多少?

可以看到,是完全滿足最左首碼法則的,索引長度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沒有索引,所以即使id、phone有索引,索引也會失效。所以虛啊喲針對於age也要建立索引。

然後,我們可以對age欄位建立索引。

create index idx_user_age on tb_user(age);

建立了索引之後,我們再次執行上述的SQL語句,看看前後執行計劃的變化。

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

(5). 數據分佈影響

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

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

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

經過測試我們發現,相同的SQL語句,只是傳入的欄位值不同,最終的執行計劃也完全不一樣,就是因為MySQL在查詢時,會評估使用索引的效率與走全表掃描的效率,如果走全表掃描更快,則放棄索引,走全表掃描。因為索引是用來索引少量數據的,如果通過索引查詢返回大批量的數據,則還不如全表掃描來的快,此時索引就會失效。

接下來,我們再來看看is null 與 is not null 操作是否走索引。

執行如下兩條語句:

explain select * from tb_user where profession is null;

explain select * from tb_user where profession is not null;

接下來,我們做一個操作將profession欄位值全部更新為null。

然後,再次執行上述的兩條SQL,查看SQL語句的執行計劃。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # Scala基礎篇 ## 數據類型 下表中列出的數據類型都是對象,可以直接對它們調用方法。 | 數據類型 | 描述 | | | | | Byte | 8位有符號補碼整數。數值區間為 -128 到 127 | | Short | 16位有符號補碼整數。數值區間為 -32768 到 32767 | | ...
  • 在Revit自帶的導出功能中,我們可以知道,Revit可以導出如下格式文件: 他們分別對應的API在Document類下麵,主要包含以下方法 1 Export(String, String, MassGBXMLExportOptions) 從體量模型文檔中導出gbXML文件。 2 Export(St ...
  • 上次老周扯了有關主、從實體的話題,本篇咱們再挖一下,主、從實體之間建立的關係,跟咱們常用的一對一、一對多這些關係之間有什麼不同。 先看看咱們從學習資料庫開始就特熟悉的常用關係——多對多、一對一、一對多說起。數據實體之間會建立什麼樣的關係,並不是規則性的,而是要看數據的功能。比如你家養的狗狗和水果(你 ...
  • Redis是一個開源的、高性能的、基於記憶體的鍵值資料庫,它支持多種數據結構,如字元串、列表、集合、散列、有序集合等。其中,Redis的散列(Hash)結構是一個常用的結構,今天跟大家分享一個我的日常操作,如何使用Redis的散列(Hash)結構來緩存和查詢對象的屬性值,以及如何用Lambda表達式樹 ...
  • # 使用Back推送消息到你的iPhone # 前言 我的好友看了我的博客,給我提了個需求,讓我搞個網站通知,我開始以為就是評論回覆然後發送郵件通知。不過他告訴我網站通知是,當有人評論或者留言後,會通知到我這邊來,消息是實時通知的,他說用的是Back,不需要發郵件,然後發了個GitHub鏈接給我,我 ...
  • # .NET6使用RabbitMQ學習 [TOC] ## 前提 前段時間上班無事,上網衝浪看到了消息隊列RabbitMQ,就想著學習一下,網上看了點資料在嗶哩嗶哩上看的到codeman講的一個rabbitmq的視頻,就跟著仔細學習一下,敲一下代碼。視頻地址: [rabbitmq視頻](【【2021最 ...
  • # shell腳本-lnmp一鍵部署 創建文件lnmp.sh ``` vim lnmp.sh ``` ``` #!/bin/bash #描述:LNMP網站架構部署腳本 cat /dev/null echo "創建Nginx運行用戶" groupadd www useradd -g www www - ...
  • # shell腳本-MySQL資料庫備份 ## 準備: 確保mysql服務啟動 **可以通過mysqldump命令來備份資料庫** 1.mysqldump 命令語法: 使用 mysqldump 命令備份一個資料庫的語法格式如下: ``` mysqldump -u username -p dbpass ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...