第四講:深入淺出索引(下)

来源:https://www.cnblogs.com/guixiangyyds/p/18314808/mysql4
-Advertisement-
Play Games

MDB (Lightning Memory-Mapped Database) 是一個高性能的嵌入式鍵值存儲資料庫,由Symas Corporation開發,並作為OpenLDAP項目的一部分發佈。LMDB被設計為輕量級、快速且可靠,適合在各種應用環境中使用,從伺服器端應用到移動設備和嵌入式系統。 L ...


目錄

第四講:深入淺出索引(下)

img

引入

​ 在上一篇文章中,我和你介紹了 InnoDB 索引的數據結構模型,今天我們再繼續聊聊跟 MySQL 索引有關的概念。

拋出問題:

​ 在開始這篇文章之前,我們先來看一下這個問題:在下麵這個表 T 中,如果我執行

 select * from T where k between 3 and 5;

​ 需要執行幾次樹的搜索操作,會掃描多少行?

​ 下麵是這個表的初始化語句。

mysql> create table T (
ID int primary key,
k int NOT NULL DEFAULT 0, 
s varchar(16) NOT NULL DEFAULT '',
index k(k))
engine=InnoDB;

insert into T values(100,1, 'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');

img

解決問題:

​ 現在,我們一起來看看這條 SQL 查詢語句的執行流程:

  1. 在 k 索引樹上找到 k=3 的記錄,取得 ID = 300;

  2. 再到 ID 索引樹查到 ID=300 對應的 R3;

  3. 在 k 索引樹取下一個值 k=5,取得 ID=500;

  4. 再回到 ID 索引樹查到 ID=500 對應的 R4;

  5. 在 k 索引樹取下一個值 k=6,不滿足條件,迴圈結束。

​ 在這個過程中,回到主鍵索引樹搜索的過程,我們稱為回表。可以看到,這個查詢過程讀了 k 索引樹的 3 條記錄(步驟 1、3 和 5),回表了兩次(步驟 2 和 4)。

​ 在這個例子中,由於查詢結果所需要的數據只在主鍵索引上有,所以不得不回表。那麼,有沒有可能經過索引優化,避免回表過程呢?

覆蓋索引

​ 如果執行的語句是 select ID from T where k between 3 and 5,這時只需要查 ID 的值,而 ID 的值已經在 k 索引樹上了,因此可以直接提供查詢結果,不需要回表。也就是說,在這個查詢裡面,索引 k 已經“覆蓋了”我們的查詢需求,我們稱為覆蓋索引。

由於覆蓋索引可以減少樹的搜索次數,顯著提升查詢性能,所以使用覆蓋索引是一個常用的性能優化手段。

​ 需要註意的是,在引擎內部使用覆蓋索引在索引 k 上其實讀了三個記錄,R3~R5(對應的索引 k 上的記錄項),但是對於 MySQL 的 Server 層來說,它就是找引擎拿到了兩條記錄,因此 MySQL 認為掃描行數是 2。

​ 備註:關於如何查看掃描行數的問題,我將會在第 16 文章《如何正確地顯示隨機消息?》中,和你詳細討論。

引申:

​ 基於上面覆蓋索引的說明,我們來討論一個問題:在一個市民信息表上,是否有必要將身份證號和名字建立聯合索引?

​ 假設這個市民表的定義是這樣的:

CREATE TABLE `tuser` (
  `id` int(11) NOT NULL,
  `id_card` varchar(32) DEFAULT NULL,
  `name` varchar(32) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `ismale` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `id_card` (`id_card`),
  KEY `name_age` (`name`,`age`)
) ENGINE=InnoDB

​ 我們知道,身份證號是市民的唯一標識。也就是說,如果有根據身份證號查詢市民信息的需求,我們只要在身份證號欄位上建立索引就夠了。而再建立一個(身份證號、姓名)的聯合索引,是不是浪費空間?

​ 如果現在有一個高頻請求,要根據市民的身份證號查詢他的姓名,這個聯合索引就有意義了。它可以在這個高頻請求上用到覆蓋索引不再需要回表查整行記錄,減少語句的執行時間。

​ 當然,索引欄位的維護總是有代價的。因此,在建立冗餘索引來支持覆蓋索引時就需要權衡考慮了。這正是業務 DBA,或者稱為業務數據架構師的工作。最左首碼原則

最左首碼原則

​ 看到這裡你一定有一個疑問,如果為每一種查詢都設計一個索引,索引是不是太多了。

​ 如果我現在要按照市民的身份證號去查他的家庭地址呢?雖然這個查詢需求在業務中出現的概率不高,但總不能讓它走全表掃描吧?反過來說,單獨為一個不頻繁的請求創建一個(身份證號,地址)的索引又感覺有點浪費。應該怎麼做呢?

(批註:這是在沒有以身份證號為主鍵的情況下所說的“全表掃描”)

​ 這裡,我先和你說結論吧。B+ 樹這種索引結構,可以利用索引的“最左首碼”,來定位記錄。

示例:

img

​ 可以看到,索引項是按照索引定義裡面出現的欄位順序排序的。當你的邏輯需求是查到所有名字是“張三”的人時,可以快速定位到 ID4,然後向後遍歷得到所有需要的結果。

​ 如果你要查的是所有名字第一個字是“張”的人,你的 SQL 語句的條件是"where name like ‘張 %’"。這時,你也能夠用上這個索引,查找到第一個符合條件的記錄是 ID3,然後向後遍歷,直到不滿足條件為止。

​ 可以看到,不只是索引的全部定義,只要滿足最左首碼,就可以利用索引來加速檢索。這個最左首碼可以是聯合索引的最左 N 個欄位,也可以是字元串索引的最左 M 個字元。

​ 基於上面對最左首碼索引的說明,我們來討論一個問題:在建立聯合索引的時候,如何安排索引內的欄位順序。

​ 這裡我們的評估標準是,索引的復用能力。因為可以支持最左首碼,所以當已經有了 (a,b) 這個聯合索引後,一般就不需要單獨在 a 上建立索引了。因此,第一原則是,如果通過調整順序,可以少維護一個索引,那麼這個順序往往就是需要優先考慮採用的。

​ 所以現在你知道了,這段開頭的問題里,我們要為高頻請求創建 (身份證號,姓名)這個聯合索引,並用這個索引支持“根據身份證號查詢地址”的需求。

​ 那麼,如果既有聯合查詢,又有基於 a、b 各自的查詢呢?查詢條件裡面只有 b 的語句,是無法使用 (a,b) 這個聯合索引的,這時候你不得不維護另外一個索引,也就是說你需要同時維護 (a,b)、(b) 這兩個索引。

(批註:用不上最左首碼,如果是高頻的查詢,就再建一個單獨的索引,當然我們可以選擇交換順序,爭取建立的索引占用的記憶體越小)

​ 這時候,我們要考慮的原則就是空間了。比如上面這個市民表的情況,name 欄位是比 age 欄位大的 ,那我就建議你創建一個(name,age) 的聯合索引和一個 (age) 的單欄位索引。

索引下推

示例:

​ 上一段我們說到滿足最左首碼原則的時候,最左首碼可以用於在索引中定位記錄。這時,你可能要問,那些不符合最左首碼的部分,會怎麼樣呢?

​ 我們還是以市民表的聯合索引(name, age)為例。如果現在有一個需求:檢索出表中“名字第一個字是張,而且年齡是 10 歲的所有男孩”。那麼,SQL 語句是這麼寫的:

mysql> select * from tuser where name like '張%' and age=10 and ismale=1;

​ 你已經知道了首碼索引規則,所以這個語句在搜索索引樹的時候,只能用 “張”,找到第一個滿足條件的記錄 ID3。當然,這還不錯,總比全表掃描要好

​ 然後呢?

​ 當然是判斷其他條件是否滿足。

​ 在 MySQL 5.6 之前,只能從 ID3 開始一個個回表。到主鍵索引上找出數據行,再對比欄位值。

​ 而 MySQL 5.6 引入的索引下推優化(index condition pushdown), 可以在索引遍歷過程中,對索引中包含的欄位先做判斷,直接過濾掉不滿足條件的記錄,減少回表次數。

​ 下麵是這兩個過程的執行流程圖。

img

img

​ 圖中每一個虛線箭頭表示回表一次。

分析:

​ 第一張圖:在 (name,age) 索引裡面我特意去掉了 age 的值,這個過程 InnoDB 並不會去看 age 的值,只是按順序把“name 第一個字是’張’”的記錄一條條取出來回表。因此,需要回表 4 次

​ 第一張圖跟第二張圖的區別是,InnoDB 在 (name,age) 索引內部就判斷了 age 是否等於 10,對於不等於 10 的記錄,直接判斷並跳過。在我們的這個例子中,只需要對 ID4、ID5 這兩條記錄回表取數據判斷,就只需要回表 2 次。

小結

​ 今天這篇文章,我和你繼續討論了資料庫索引的概念,包括了覆蓋索引、首碼索引、索引下推。你可以看到,在滿足語句需求的情況下, 儘量少地訪問資源是資料庫設計的重要原則之一。我們在使用資料庫的時候,尤其是在設計表結構時,也要以減少資源消耗作為目標。

深入:

1.覆蓋索引的第二個使用:在聯合索引上使用,也可以避免回表。這個也可以應用到項目開發中。

2.數據中身份證 + 姓名占比比較高,一般不會再創建這樣的索引,真有高頻查詢也會使用緩存組件;當然作者這裡只是給大家舉個例子說明覆蓋索引的,明白覆蓋索引就行

3.給id_card 和 name建立聯合索引後,name的值也會被保存在id_card索引樹的節點上,這樣根據給定id_card的值找到的對應行時,就可以直接獲取到name了,而不需要拿著對應的主鍵再進行回表操作。

4.最左首碼原則是幹嘛的呢,很簡單,就是為瞭解決“為一個不頻繁的請求創建一個索引感覺很浪費”的問題的

5.B+ 樹這種索引結構,可以利用索引的“最左首碼”,來定位記錄。

6.最左首碼原則:當查詢條件只有b時,a沒有被用到,導致索引失效

問題:

​ 實際上主鍵索引也是可以使用多個欄位的。DBA 小呂在入職新公司的時候,就發現自己接手維護的庫裡面,有這麼一個表,表結構定義類似這樣的:

CREATE TABLE `geek` (
  `a` int(11) NOT NULL,
  `b` int(11) NOT NULL,
  `c` int(11) NOT NULL,
  `d` int(11) NOT NULL,
  PRIMARY KEY (`a`,`b`),
  KEY `c` (`c`),
  KEY `ca` (`c`,`a`),
  KEY `cb` (`c`,`b`)
) ENGINE=InnoDB;

​ 公司的同事告訴他說,由於歷史原因,這個表需要 a、b 做聯合主鍵,這個小呂理解了。

​ 但是,學過本章內容的小呂又納悶了,既然主鍵包含了 a、b 這兩個欄位,那意味著單獨在欄位 c 上創建一個索引,就已經包含了三個欄位了呀,為什麼要創建“ca”“cb”這兩個索引?

​ 同事告訴他,是因為他們的業務裡面有這樣的兩種語句

select * from geek where c=N order by a limit 1;
select * from geek where c=N order by b limit 1;

(從geek表中找出c列值等於某個特定值的所有行,然後按照a列的值進行排序,並只返回排序後的第一行數據。)

​ 我給你的問題是,這位同事的解釋對嗎,為了這兩個查詢模式,這兩個索引是否都是必須的?為什麼呢?

答案:

表記錄

–a--|–b--|–c--|–d--

1 2 3 d

1 3 2 d

1 4 3 d

2 1 3 d

2 2 2 d

2 3 4 d

主鍵 a,b 的聚簇索引組織順序相當於 order by a,b ,也就是先按 a 排序,再按 b 排序,c 無序。(標記1)

索引 ca 的組織是先按 c 排序,再按 a 排序,同時記錄主鍵

–c--|–a--|–主鍵部分b-- (註意,這裡不是 ab,而是只有 b)

2 1 3

2 2 2

3 1 2

3 1 4

3 2 1

4 2 3

這個跟索引 c 的數據是一模一樣的。

索引 cb 的組織是先按 c 排序,在按 b 排序,同時記錄主鍵

–c--|–b--|–主鍵部分a-- (同上)

2 2 2

2 3 1

3 1 2

3 2 1

3 4 1

4 3 2

所以,結論是 ca 可以去掉,cb 需要保留。

深入分析上題:

1.索引“cb”有必要,“ca”沒必要,因為索引“c”會包含主鍵值,where c=x order by a,因為c是固定值,所以 a 值是順序的,可以避免排序。但是 where c=x order by b,因為a 值不固定,所以 b 值不是按順序的(在標記一提到),單純靠索引“c”無法避免排序,所以需要 “cb”索引。

2.InnoDB會把主鍵欄位放到索引定義欄位後面, 當然同時也會去重。 所以,當主鍵是(a,b)的時候, 定義為c的索引,實際上是(c,a,b); 定義為(c,a)的索引,實際上是(c,a,b) 你看著加是相同的 ps 定義為(c,b)的索引,實際上是(c,b,a)

3.解析: c欄位上創建了索引,所以最終的業主節點值為(c,a,b),也就是索引節點+主鍵; ca上創建了索引,由於a是主鍵的一部分,所以葉子節點不會再重覆添加a (c,a,a,b),而是(c,a,b),那麼這顆索引樹和1中的重覆了,所以可以去掉; cb上創建索引,葉子節點為(c,b,a)

標記一處:如果a一樣的情況下,才會根據b去排序


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

-Advertisement-
Play Games
更多相關文章
  • 本文介紹了:外設寄存器查找 ① 名稱 ② 偏移地址 ③ 寄存器位表 ④ 位功能說明 寄存器基本操作 C語言的置位和清零 具體方法 設置GPIO流程 給寄存器賦值 帶參數巨集 STM32F1xx 晶元識別 存儲器映射 寄存器映射 讓GPIOB埠的16個引腳輸出高電平,要怎麼實現? STM32寄存器映射... ...
  • 1、設置tabsize縮進為4修改點兩個位置,然後搜索"detectindentation"將前面的勾選取消 二、配置c語言格式化代碼 1、打開設置,選擇用戶配置,找到文本編輯器 "Default Formatter"(如果找不到可以直接在上方搜索) 安裝了C/C++插件後可以選擇:C/C++ms- ...
  • 光貓在家裡只能擔當撥號上網的角色嗎?不,稍加改造就是一臺NAS! 在這裡,使用natmap映射IPV4,使用nginx+njs+vList5打造線上文件預覽+管理,使用nginx+davext實現遠程文件管理 甚至還可以使用aria2下載磁力做下載器,使用clash讓光貓變成真的貓!只要你敢想,光貓... ...
  • Ventoy和UltraISO都是常用於製作啟動盤的工具,但它們在功能和使用方式上有一些顯著的區別。 Ventoy Ventoy是一個開源的啟動盤製作工具,其主要特點包括: 多鏡像支持:用戶可以將多個ISO/WIM/IMG/VHD(x)/EFI等類型的文件直接拷貝到U盤,Ventoy會在啟動時顯示一 ...
  • 適用版本: 適用於TOS 5.0.xxx、TOS5.1.xxx版本。 適用機型: TNAS型號(除F2-210、F4-210) 故障現象: 當TNAS宕機導致網頁不可訪問且PC無法搜索到該設備時,重啟後TOS網頁的系統報告缺失相關日誌,不利於異常原因的分析。 故障原因: 當TNAS設備出現宕機重啟T ...
  • Oracle資料庫中如果標準大頁設置不合理,可能導致物理記憶體被浪費掉。下麵介紹一個案例: 查看標準大頁的信息,如下所示: $ grep HugePages /proc/meminfoAnonHugePages: 0 kBShmemHugePages: 0 kBFileHugePages: 0 kBH ...
  • 上一篇文章《使用 Categraf 快速建設 MySQL 監控,同時介紹夜鶯模板中心》我們已經瞭解瞭如何使用夜鶯配合 Categraf 監控 MySQL,本節我們重點看看 Redis 監控的實操方案。 Redis 監控資料 首先還是去模板中心找到 Redis 插件相關的說明和各類模板,菜單位置在:集 ...
  • 問題 MGR 中,新節點在加入時,為了與組內其它節點的數據保持一致,它會首先經歷一個分散式恢復階段。在這個階段,新節點會隨機選擇組內一個節點(Donor)來同步差異數據。 在 MySQL 8.0.17 之前,同步的方式只有一種,即基於 Binlog 的非同步複製,這種方式適用於差異數據較少或需要的 B ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...