MySQL索引-回表-覆蓋索引


SQL語句是如何執行的?

 連接器:第一步,先連接到這個數據庫上,這時候接待你的就是連接器。連接器負責跟客戶端建立連接、獲取權限、維持和管理連接。連接命令一般是這么寫的: mysql -h$ip -P$port -u$user -p 連接建立完成后,你就可以執行 select 語句了。

查詢緩存:第二步執行邏輯,MySQL 拿到一個查詢請求后,會先到查詢緩存看看,之前是不是執行過這條語句。之前執行過的語句及其結果可能會以 key-value 對的形式,被直接緩存在內存中。key 是查詢的語句,value 是查詢的結果。如果你的查詢能夠直接在這個緩存中找到 key,那么這個 value 就會被直接返回給客戶端。但是大多數情況下我會建議你不要使用查詢緩存,為什么呢?因為查詢緩存往往弊大於利。

分析器:先會做“詞法分析”。你輸入的是由多個字符串和空格組成的一條 SQL 語句,MySQL 需要識別出里面的字符串分別是什么,代表什么。MySQL 從你輸入的"select"這個關鍵字識別出來,這是一個查詢語句。它也要把字符串“T”識別成“表名 T”,把字符串“ID”識別成“列 ID”。

優化器:是在表里面有多個索引的時候,決定使用哪個索引;或者在一個語句有多表關聯(join)的時候,決定各個表的連接順序。

執行器:MySQL 通過分析器知道了你要做什么,通過優化器知道了該怎么做,於是就進入了執行器階段,開始執行語句。開始執行的時候,要先判斷一下你對這個表 T 有沒有執行查詢的權限,如果沒有,就會返回沒有權限的錯誤,如下所示 (在工程實現上,如果命中查詢緩存,會在查詢緩存返回結果的時候,做權限驗證。查詢也會在優化器之前調用 precheck 驗證權限)。

索引分類?

單列索引,聚合索引,全文索引

單列索引又分:主鍵索引,唯一索引,普通索引

主鍵索引 :又稱聚簇索引,主鍵索引的葉子節點存的是整行數據,根據主鍵查詢可以直接查詢出記錄,沒有回表的操作。

普通索引: 又稱二級索引,非主鍵索引的葉子節點內容是主鍵的值,基於非主鍵索引的查詢需要多掃描一棵索引樹(回表)。

覆蓋索引查詢的結果直接在索引樹上 (select 字段在索引樹上),不需要回表。覆蓋索引是指,索引上的信息足夠滿足查詢請求,不需要再回到主鍵索引上去取數據。由於覆蓋索引可以減少樹的搜索次數,顯著提升查詢性能,所以使用覆蓋索引是一個常用的性能優化手段,explain查詢計划優化章節,即explain的輸出結果Extra字段為Using index時,能夠觸發索引覆蓋。

 count()最優?

首先count(*)、count(主鍵 id) 和 count(1) 都表示返回滿足條件的結果集的總行數;

至於分析性能差別的時候,你可以記住這么幾個原則:

1)server 層要什么就給什么;

2)InnoDB 只給必要的值;

3)現在的優化器只優化了count(*)的語義為取行數

count(主鍵 id) :InnoDB 引擎會遍歷整張表,把每一行的 id 值都取出來,返回給 server 層。server 層拿到 id 后,判斷是不可能為空的,就按行累加。

count(1) :InnoDB 引擎遍歷整張表,但不取值。server 層對於返回的每一行,放一個數字“1”進去,判斷是不可能為空的,按行累加。

count(*): 是例外,並不會把全部字段取出來,而是專門做了優化,不取值。count(*) 肯定不是 null,按行累加

count(字段):則表示返回滿足條件的數據行里面,參數“字段”不為 NULL 的總個數

count性能比較,count(字段)<count(主鍵)<count(1)基本等於count(*),但是MySQL目前只對count(*)進行了優化,因此建議使用count(*)  

 排序原理?

MySQL會為每個線程分配一個sort_buffer,當查詢出符合條件的記錄后,會將記錄存放到sort_buffer。 當sort_buffer_size夠用的時候,就直接在內存中排序,如果當sort_buffer_size不夠用的時候,就會借助磁盤進行排序。 一般MySQL會優先選擇全字段排序,只有當記錄數量比較大時,才使用_rowid排序,使用Rowid排序會有回表的過程,因此性能也會有所降低。

 回表查詢?

一次查詢索引樹就能到得結果的,不會回表查詢,需要多次查詢索引樹,才能得到需要的結果稱為回表查詢。

如粉紅色路徑,需要掃碼兩遍索引樹:

(1)先通過普通索引定位到主鍵值id=5;

(2)在通過聚集索引定位到行記錄;

這就是所謂的回表查詢,先定位主鍵值,再定位行記錄,它的性能較掃一遍索引樹更低。

例如:

CREATE TABLE t (
id INT PRIMARY KEY, 
age INT NOT NULL DEFAULT 0,
addr VARCHAR(64), 
INDEX age(age)
) ENGINE = INNODB;

SELECT * FROM t WHERE id=1;

使用主鍵索引 SELECT * FROM t WHERE age BETWEEN 3 AND 5;使用普通索引age,並且會有回表操作,select * 查詢的字段不全在索引樹上,需要通過主鍵再次去主鍵索引樹上查找,所以有回表操作

SELECT id FROM t WHERE age BETWEEN 3 AND 5;使用覆蓋索引,沒有回表操作

 覆蓋索引?

白話就講解就是 在非聚集索引上面是否能一次性獲取select的字段,能獲取就為覆蓋索引,否則就不是

create table user (
id int primary key,
name varchar(20),
sex varchar(5),
key(name)
)engine=innodb;

 能夠命中name索引,索引葉子節點存儲了主鍵id,通過name的索引樹即可獲取id和name,無需回表,符合索引覆蓋,效率較高

  

 能夠命中name索引,索引葉子節點存儲了主鍵id,但sex字段必須回表查詢才能獲取到,不符合索引覆蓋,需要再次通過id值掃碼聚集索引獲取sex字段,效率會降低。

 

   

普通索引VS唯一索引?

查詢

唯一索引:掃描索引樹,如果找到,則停止掃描。

普通索引:掃描索引樹,如果找到,則繼續掃描,直到找不到。

更新

唯一索引:用不到change buffer

普通索引:可能會用到change buffer,提高性能

1.新增:唯一索引和普通索引的查詢性能差距微乎其微

2.更新: 為了說明普通索引和唯一索引對更新語句性能的影響這個問題,先了解一下 change buffer。當需要更新一個數據頁時,如果數據頁在內存中就直接更新,而如果這個數據頁還沒有在內存中的話,在不影響數據一致性的前提下,InooDB 會將這些更新操作緩存在 change buffer 中,這樣就不需要從磁盤中讀入這個數據頁了。在下次查詢需要訪問這個數據頁的時候,將數據頁讀入內存,然后執行 change buffer 中與這個頁有關的操作。通過這種方式就能保證這個數據邏輯的正確性。需要說明的是,雖然名字叫作 change buffer,實際上它是可以持久化的數據。也就是說,change buffer 在內存中有拷貝,也會被寫入到磁盤上。將 change buffer 中的操作應用到原數據頁,得到最新結果的過程稱為 merge。除了訪問這個數據頁會觸發 merge 外,系統有后台線程會定期 merge。在數據庫正常關閉(shutdown)的過程中,也會執行 merge 操作。

組合索引?

在表中的多個字段組合上創建的索引,只有在查詢條件中使用了這些字段的左邊字段時,索引才會被使用,使用組合索引時遵循最左前綴原則。

索引下推?

MySQL 5.6 引入的索引下推優化, 可以在索引遍歷過程中,對索引中包含的字段先做判斷,直接過濾掉不滿足條件的記錄,優化減少回表次數

CREATE TABLE `tuser` ( 
`id` int(11) NOT NULL, 
`name` varchar(32) DEFAULT NULL,
 `age` int(11) DEFAULT NULL, 
`ismale` tinyint(1) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `name_age` (`name`,`age`) 
) ENGINE=InnoDB;
SELECT * from tuser where name like '陳%' and age=20

Mysql5.6及之前版本

 

 

 會忽略age這個字段,直接通過name進行查詢,在(name,age)這課樹上查找到了兩個結果,id分別為2,1,然后拿着取到的id值一次次的回表查詢,因此這個過程需要回表兩次

 

Mysql5.6及之后版本

 

 InnoDB並沒有忽略age這個字段,而是在索引內部就判斷了age是否等於20,對於不等於20的記錄直接跳過,因此在(name,age)這棵索引樹中只匹配到了一個記錄,此時拿着這個id去主鍵索引樹中回表查詢全部數據,這個過程只需要回表一次

 

 根據explain解析結果可以看出Extra的值為Using index condition,表示已經使用了索引下推。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM