MySQL 回表

来源:https://www.cnblogs.com/taojietaoge/archive/2022/04/23/16167188.html
-Advertisement-
Play Games

MySQL 回表 五花馬,千金裘,呼兒將出換美酒,與爾同銷萬古愁。 一、簡述 回表,顧名思義就是回到表中,也就是先通過普通索引掃描出數據所在的行,再通過行主鍵ID 取出索引中未包含的數據。所以回表的產生也是需要一定條件的,如果一次索引查詢就能獲得所有的select 記錄就不需要回表,如果select ...


MySQL 回表

 

    五花馬,千金裘,呼兒將出換美酒,與爾同銷萬古愁。

 

一、簡述

回表,顧名思義就是回到表中,也就是先通過普通索引掃描出數據所在的行,再通過行主鍵ID 取出索引中未包含的數據。所以回表的產生也是需要一定條件的,如果一次索引查詢就能獲得所有的select 記錄就不需要回表,如果select 所需獲得列中有其他的非索引列,就會發生回表動作。即基於非主鍵索引的查詢需要多掃描一棵索引樹

二、InnoDB 引擎有兩大類索引

要弄明白回表,首先得瞭解 InnoDB 兩大索引,即聚集索引 (clustered index)和普通索引(secondary index)。

聚集索引 clustered index

InnoDB聚集索引的葉子節點存儲行記錄,因此, InnoDB必須要有且只有一個聚集索引。

  • 如果表定義了主鍵,則Primary Key 就是聚集索引;
  • 如果表沒有定義主鍵,則第一個非空唯一索引(Not NULL Unique)列是聚集索引;
  • 否則,InnoDB會創建一個隱藏的row-id作為聚集索引;

普通索引(secondary index

普通索引也叫二級索引,除聚簇索引外的索引都是普通索引,即非聚簇索引。

InnoDB的普通索引葉子節點存儲的是主鍵(聚簇索引)的值,而MyISAM的普通索引存儲的是記錄指針。

三、回表示例

數據準備

先創建一張表  t_back_to_table ,表中id 為主鍵索引即聚簇索引,drinker_id為普通索引。

CREATE TABLE t_back_to_table (

id INT PRIMARY KEY,

drinker_id INT NOT NULL,

drinker_name VARCHAR ( 15 ) NOT NULL,

drinker_feature VARCHAR ( 15 ) NOT NULL,

INDEX ( drinker_id )

) ENGINE = INNODB; 

再執行下麵的 SQL 語句,插入四條測試數據。

INSERT INTO t_back_to_table ( id, drinker_id, drinker_name, drinker_feature )

VALUES

( 1, 2, '廣西-玉林', '喝到天亮' ),

( 2, 1, '廣西-河池', '白酒三斤半啤酒隨便灌' ),

( 3, 3, '廣西-貴港', '喝到晚上' ),

( 4, 4, '廣西-柳州', '喝酒不吃飯' );

NO回表case 

使用主鍵索引id查詢出id 3 的數據。

EXPLAIN SELECT * FROM t_back_to_table WHERE id = 3;

執行 EXPLAIN SELECT * FROM t_back_to_table WHERE id = 3,這條 SQL 語句就不需要回表。

因為是根據主鍵的查詢方式,則只需要搜索 ID 這棵 B+ ,樹上的葉子節點存儲了行記錄,根據這個唯一的索引,MySQL 就能確定搜索的記錄。

回表case 

使用 drinker_id 這個索引來查詢 drinker_id = 3 的記錄時就會涉及到回表。

SELECT * FROM t_back_to_table WHERE drinker_id = 3;

因為通過 drinker_id 這個普通索引查詢方式,則需要先搜索 drinker_id 索引樹(該索引樹上記錄著主鍵ID的值),然後得到主鍵 ID 的值為 3,再到 ID 索引樹搜索一次。這個過程雖然用了索引,但實際上底層進行了兩次索引查詢,這個過程就稱為回表。

回表小結

  • 對比發現,基於非主鍵索引的查詢需要多掃描一棵索引樹,先定位主鍵值,再定位行記錄,它的性能較掃一遍索引樹更低。
  • 在應用中應該儘量使用主鍵查詢,這裡表中就四條數據,如果數據量大的話,就可以明顯的看出使用主鍵查詢效率更高。
  • 使用聚集索引(主鍵或第一個唯一索引)就不會回表,普通索引就會回表。

四、索引存儲結構

InnoDB 引擎的聚集索引和普通索引都是B+Tree 存儲結構只有葉子節點存儲數據

  • 新的B+樹結構沒有在所有的節點里存儲記錄數據,而是只在最下層的葉子節點存儲,上層的所有非葉子節點只存放索引信息,這樣的結構可以讓單個節點存放更多索引值,增大Degree 的值,提高命中目標記錄的幾率。
  • 這種結構會在上層非葉子節點存儲一部分冗餘數據,但是這樣的缺點都是可以容忍的,因為冗餘的都是索引數據,不會對記憶體造成大的負擔。

聚簇索引

id 是主鍵,所以是聚簇索引,其葉子節點存儲的是對應行記錄的數據

聚簇索引存儲結構

如果查詢條件為主鍵(聚簇索引),則只需掃描一次B+樹即可通過聚簇索引定位到要查找的行記錄數據。

如:

SELECT * FROM t_back_to_table WHERE id = 1;

查找過程:

聚簇索引查找過程 

普通索引

drinker_id 是普通索引(二級索引),非聚簇索引葉子節點存儲的是聚簇索引的值,即主鍵ID的值

普通索引存儲結構

如果查詢條件為普通索引(非聚簇索引),需要掃描兩次B+樹

  • 第一次掃描通過普通索引定位到聚簇索引的值
  • 第二次掃描通過第一次掃描獲得的聚簇索引的值定位到要查找的行記錄數據。

如:

SELECT * FROM t_back_to_table WHERE drinker_id = 1;

(1)第一步,先通過普通索引定位到主鍵值id=1

2第二步,回表查詢,再通過定位到的主鍵值即聚集索引定位到行記錄數據。

普通索引查找過程

五、如何防止回表

既然我們知道了有回表這麼回事,肯定就要儘可能去防微杜漸。最常見的防止回表手段就是索引覆蓋,通過索引打敗索引。

索引覆蓋

為什麼可以使用索引打敗索引防止回表呢?因為其只需要在一棵索引樹上就能獲取SQL所需的所有列數據,無需回表查詢

例如:SELECT * FROM t_back_to_table WHERE drinker_id = 1;

如何實現覆蓋索引?

常見的方法是將被查詢的欄位,建立到聯合索引中。

解釋性SQL的explain的輸出結果Extra欄位為Using index時表示觸發了索引覆蓋。

No覆蓋索引case1

繼續使用之前創建的 t_back_to_table 表,通過普通索引drinker_id 查詢id 和 drinker_id 列。

EXPLAIN SELECT id, drinker_id FROM t_back_to_table WHERE drinker_id = 1;

explain分析:為什麼沒有創建覆蓋索引Extra欄位仍為Using index,因為drinker_id是普通索引,使用到了drinker_id索引,在上面有提到普通索引的葉子節點保存了聚簇索引的值所以通過一次掃描B+樹即可查詢到相應的結果,這樣就實現了隱形的覆蓋索引即沒有人為的建立聯合索引。(drinker_id索引上包含了主鍵索引的值

No覆蓋索引case2

繼續使用之前創建的 t_back_to_table 表,通過普通索引drinker_id查詢 iddrinker_iddrinker_feature列數據。

EXPLAIN SELECT id, drinker_id, drinker_feature FROM t_back_to_table WHERE drinker_id = 1;

explain分析:drinker_id是普通索引其葉子節點上僅包含主鍵索引的值,而 drinker_feature 不在索引樹上,所以通過drinker_id 索引在查詢到id和drinker_id的值後,需要根據主鍵id 進行回表查詢,得到 drinker_feature 的值。此時的Extra列的NULL表示進行了回表查詢。

覆蓋索引case

為了實現索引覆蓋,需要建組合索引 idx_drinker_id_drinker_feature(drinker_id,drinker_feature)

#刪除索引 drinker_id

DROP INDEX drinker_id ON t_back_to_table;

#建立組合索引

CREATE INDEX idx_drinker_id_drinker_feature on t_back_to_table(`drinker_id`,`drinker_feature`);

繼續使用之前創建的 t_back_to_table 表,通過覆蓋索引 idx_drinker_id_drinker_feature 查詢 iddrinker_iddrinker_feature列數據。

EXPLAIN SELECT id, drinker_id, drinker_feature FROM t_back_to_table WHERE drinker_id = 1;

explain分析:此時欄位drinker_iddrinker_feature是組合索引idx_drinker_id_drinker_feature,查詢的欄位id、drinker_iddrinker_feature的值剛剛都在索引樹上,只需掃描一次組合索引B+樹即可,這就是實現了索引覆蓋,此時的Extra欄位為Using index表示使用了索引覆蓋。

六、索引覆蓋優化SQL場景

適合使用索引覆蓋來優化SQL的場景如全表count查詢、列查詢回表和分頁查詢等。

全表count查詢優化

#首先刪除 t_back_to_table 表中的組合索引

DROP INDEX idx_drinker_id_drinker_feature ON t_back_to_table;

EXPLAIN SELECT COUNT(drinker_id) FROM t_back_to_table

explain分析:此時的Extra欄位為Null 表示沒有使用索引覆蓋。

使用索引覆蓋優化,創建drinker_id欄位索引。

#創建 drinker_id 欄位索引

CREATE INDEX idx_drinker_id on t_back_to_table(drinker_id);

EXPLAIN SELECT COUNT(drinker_id) FROM t_back_to_table

explain分析:此時的Extra欄位為Using index表示使用了索引覆蓋。

列查詢回表優化

前文在描述索引覆蓋使用的例子就是列查詢回表優化。

例如:

SELECT id, drinker_id, drinker_feature FROM t_back_to_table WHERE drinker_id = 1;

使用索引覆蓋:建組合索引 idx_drinker_id_drinker_feature on t_back_to_table(`drinker_id`,`drinker_feature`)即可

分頁查詢優化

#首先刪除 t_back_to_table 表中的索引 idx_drinker_id

DROP INDEX idx_drinker_id ON t_back_to_table;

EXPLAIN SELECT id, drinker_id, drinker_name, drinker_feature FROM t_back_to_table ORDER BY drinker_id limit 200, 10;

explain分析:因為 drinker_id 欄位不是索引,所以在分頁查詢需要進行回表查詢,此時Extra為U sing filesort 文件排序,查詢性能低下。

使用索引覆蓋:建組合索引 idx_drinker_id_drinker_name_drinker_feature

#建立組合索引 idx_drinker_id_drinker_name_drinker_feature (`drinker_id`,`drinker_name`,`drinker_feature`)

CREATE INDEX idx_drinker_id_drinker_name_drinker_feature on t_back_to_table(`drinker_id`,`drinker_name`,`drinker_feature`);

再次根據 drinker_id 分頁查詢:

EXPLAIN SELECT id, drinker_id, drinker_name, drinker_feature FROM t_back_to_table ORDER BY drinker_id limit 200, 10;

explain分析:此時的Extra欄位為Using index表示使用了索引覆蓋。

 

 

 

 

五花馬     千金裘             呼兒將出換美酒 與爾同銷萬古愁  

 

 

 

 


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

-Advertisement-
Play Games
更多相關文章
  • 《零基礎學Java》 資料庫操作 資料庫基礎 資料庫是一種存儲結構,允許使用各種格式 輸入、處理、檢索 數據,且不用在每次需要數據時重新輸入數據。 select 語句: select語句用於查詢數據表中的數據。 select 語句 語法: select 所選欄位列表 from 數據表名 where ...
  • 在Python的應用實踐過程中,經常會遇到字元串使用的問題,我花了點時間,專門整理了 Python 關於 string 的知識,主要內容 包括以下幾個方面: 為了方便以後查看,速查表的形式整理了 Python字元串的內容,《Python字元串速查表》(V1.0版)一共 5 頁,先來一 張圖看下概覽哈 ...
  • SunnyUI.Net, 基於 C# .Net WinForm 開源控制項庫、工具類庫、擴展類庫、多頁面開發框架 Blog: https://www.cnblogs.com/yhuse Gitee: https://gitee.com/yhuse/SunnyUI GitHub: https://git ...
  • 之前寫過一篇如使用阿裡雲上部署.NET 3.1自定義運行時的文章,吐槽一下,雖然現在已經2022年了,但是阿裡雲函數計算的支持依然停留在.NET Core 2.1,更新緩慢,由於程式解包大小的限制,也不能放太複雜的東西的上去,雖然現在.NET 6裁剪包能挺好地解決這個問題,但是心裡還是不爽。 需求 ...
  • 之前曾在《C# 中容易忽視的 Encoding.GetByteCount 記憶體問題》中提到過,可以使用 Encoding.Default.GetByteCount 方法來判斷字元是全寬(寬度為 2)還是半寬(寬度為 1)。 這個方法實際上是計算對字元編碼後產生的位元組數,只是在中文環境下,寬字元在使用 ...
  • 本筆記有特殊目錄,點擊開啟: 專有目錄 在Linux系統中編輯文本總是離不開一位老幫手——Vi。而因為其誕生的年代有些久遠,有些操作在現在看來可能有點“反直覺”。 於是我決定寫這樣一篇小筆記,記錄一下我記憶Vi的這些這些 常用 操作和指令的方法(主要靠的是英語和圖示了)。 當然,正如“好記性不如爛筆 ...
  • Vmware Station安裝Arch Linux 一、測試環境及工具 當前教程在win10環境中進行,網路為有線連接 vmware station版本為 16.2.1 arch linux鏡像為 archlinux-2022.04.05-x86_64.iso 終端工具Xshell 6,也可使用p ...
  • 一篇科普文章,介紹什麼是 Linux 信號,以及它的基本用法,內含精美圖表。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...