MySQL實戰45講 4,5

来源:https://www.cnblogs.com/ydssx7/archive/2022/07/20/16499069.html
-Advertisement-
Play Games

04 | 深入淺出索引(上) 索引的出現其實就是為了提高數據查詢的效率,就像書的目錄一樣 索引的常見模型 哈希表、有序數組和搜索樹 哈希表 User2 和 User4 根據身份證號算出來的值都是 N,但沒關係,後面還跟了一個鏈表。 需要註意的是,圖中四個 ID_card_n hash 出來的值並不是 ...


04 | 深入淺出索引(上)

索引的出現其實就是為了提高數據查詢的效率,就像書的目錄一樣

索引的常見模型

哈希表、有序數組和搜索樹

哈希表

image

User2 和 User4 根據身份證號算出來的值都是 N,但沒關係,後面還跟了一個鏈表。

需要註意的是,圖中四個 ID_card_n hash 出來的值並不是遞增的,這樣做的好處是增加新的 User 時速度會很快,只需要往後追加。但缺點是,因為不是有序的,所以哈希索引做區間查詢的速度是很慢的,需要全部掃描一遍。

所以,哈希表這種結構適用於只有等值查詢的場景,比如 Memcached 及其他一些 NoSQL 引擎。

有序數組

有序數組在等值查詢和範圍查詢場景中的性能就都非常優秀

image

數組就是按照身份證號遞增的順序保存的。這時候如果你要查 ID_card_n2 對應的名字,用二分法就可以快速得到,這個時間複雜度是 O(log(N))

同樣可以通過二分找到大於範圍和小於範圍的兩個值

更新數據的時候必須得挪動後面所有的記錄,成本太高

有序數組索引只適用於靜態存儲引擎

二叉樹


二叉搜索樹:一棵空樹,或者是具有下列性質的二叉樹:若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;二叉搜索樹的左、右子樹也分別為二叉搜索樹。

平衡二叉樹:平衡二叉樹是在二叉搜索樹的基礎上引入的,指的是結點的左子樹和右子樹的深度差不超過 1.

多叉樹:每個結點可以有多個子結點,子節點的大小從左到右依次遞增。


image

二叉搜索樹的特點是:每個節點的左兒子小於父節點,父節點又小於右兒子。這樣如果你要查 ID_card_n2 的話,按照圖中的搜索順序就是按照 UserA -> UserC -> UserF -> User2 這個路徑得到。這個時間複雜度是 O(log(N))

當然為了維持 O(log(N)) 的查詢複雜度,你就需要保持這棵樹是平衡二叉樹。為了做這個保證,更新的時間複雜度也是 O(log(N))

一棵 100 萬節點的平衡二叉樹,樹高 20。一次查詢可能需要訪問 20 個數據塊

Q:為什麼樹高20就是20個數據塊?

A:每個葉子結點就是一個塊,每個塊包含兩個數據(?),塊之間通過鏈式方式鏈接。樹高20的話,就要遍歷20個塊。因為是二叉樹結構,每次指針查找很大概率是觸發隨機磁碟讀(比如很難剛好碰上一個節點和他的左右兒子剛好相鄰)

資料庫中的所有數據都存儲於數據塊中,而葉子節點塊只是數據塊的一種,因為它位於索引b*tree結構中的最末端(root(根)-->branch(分枝)-->leaf(葉子)),所以,就稱為葉子節點塊(leaf node block)

《個人理解》參照下麵的B+樹,一個葉子塊中可以存放多條索引和數據

為了讓一個查詢儘量少地讀磁碟,就必須讓查詢過程訪問儘量少的數據塊。那麼,我們就不應該使用二叉樹,而是要使用“N 叉”樹。這裡,“N 叉”樹中的“N”取決於數據塊的大小。

Q:B+樹是一顆N叉樹,N是由什麼決定的?能否調整?
A:

  1. 通過修改page的大小來間接調整N的大小。一個節點上的所有數據都在一個page中,頁越大,每頁存放的索引就越多,N就越大。數據頁調整後,如果數據頁太小層數會太深,數據頁太大,載入到記憶體的時間和單個數據頁查詢時間會提高,需要達到平衡才行。

  2. 修改索引的大小。每個索引包括固定位元組數的Point指針和索引欄位內容,索引欄位越小,每頁能存的索引就越多,N就越大。

以 InnoDB 的一個整數欄位索引為例,這個 N 差不多是 1200。這棵樹高是 4 的時候,就可以存 1200 的 3 次方個值,這已經 17 億了。考慮到樹根的數據塊總是在記憶體中的,一個 10 億行的表上一個整數欄位的索引,查找一個值最多只需要訪問 3 次磁碟。其實,樹的第二層也有很大概率在記憶體中,那麼訪問磁碟的平均次數就更少了。

InnoDB 的索引模型

在 InnoDB 中,表都是根據主鍵順序以索引的形式存放的,這種存儲方式的表稱為索引組織表

InnoDB 使用了 B+ 樹索引模型,所以數據都是存儲在 B+ 樹中的。

假設,我們有一個主鍵列為 ID 的表,表中有欄位 k,並且在 k 上有索引。

create table T(
id int primary key, 
k int not null, 
name varchar(16),
index (k))engine=InnoDB;

表中 R1~R5 的 (ID,k) 值分別為 (100,1)、(200,2)、(300,3)、(500,5) 和 (600,6),兩棵樹的示例示意圖如下。

image

索引類型分為主鍵索引和非主鍵索引

主鍵索引的葉子節點存的是整行數據。在 InnoDB 里,主鍵索引也被稱為聚簇索引(clustered index)。

非主鍵索引的葉子節點內容是主鍵的值。在 InnoDB 里,非主鍵索引也被稱為二級索引(secondary index)。

Q:基於主鍵索引和普通索引的查詢有什麼區別?

A:

  • 如果語句是 select * from T where ID=500,即主鍵查詢方式,則只需要搜索 ID 這棵 B+ 樹
  • 如果語句是 select * from T where k=5,即普通索引查詢方式,則需要先搜索 k 索引樹,得到 ID 的值為 500,再到 ID 索引樹搜索一次。這個過程稱為回表

索引維護

B+ 樹為了維護索引有序性,在插入新值的時候需要做必要的維護。

以上面這個圖為例,

如果插入新的行 ID 值為 700,則只需要在 R5 的記錄後面插入一個新記錄

如果新插入的 ID 值為 400,就相對麻煩了,需要邏輯上挪動後面的數據,空出位置

而更糟的情況是,如果 R5 所在的數據頁已經滿了,根據 B+ 樹的演算法,這時候需要申請一個新的數據頁,然後挪動部分數據過去。這個過程稱為頁分裂

當相鄰兩個頁由於刪除了數據,利用率很低之後,會將數據頁做合併。

Q:如果插入的數據是在主鍵樹葉子結點的中間,後面的所有頁如果都是滿的狀態,是不是會造成後面的每一頁都會去進行頁分裂操作,直到最後一個頁申請新頁移過去最後一個值?

A: 不會不會,只會分裂它要寫入的那個頁面。每個頁面之間是用指針串的,改指針就好了,不需要“後面的全部挪動

ps:數據塊和數據頁的含義應該是一致的

自增主鍵

自增主鍵是指自增列上定義的主鍵,在建表語句中一般是這麼定義的: NOT NULL PRIMARY KEY AUTO_INCREMENT。

好處:(存,查,插)

  • 插入新記錄的時候可以不指定 ID 的值,系統會獲取當前 ID 最大值加 1 作為下一條記錄的 ID 值。每次插入一條新記錄,都是追加操作,都不涉及到挪動其他記錄,也不會觸發葉子節點的分裂
  • 自增欄位為整形或長整型,每個非主鍵索引的葉子節點上都是主鍵的值,主鍵長度越小,普通索引的葉子節點就越小,普通索引占用的空間也就越小。
  • 業務邏輯欄位不容易保證索引樹結點有序插入,這樣寫入成本較高。

Q:有沒有什麼場景適合用業務欄位直接做主鍵的呢?

A:典型的 KV 場景

  1. 只有一個索引;

  2. 該索引必須是唯一索引

這時候我們就要優先考慮上一段提到的“儘量使用主鍵查詢”原則,直接將這個索引設置為主鍵,可以避免每次查詢需要搜索兩棵樹。

自增ID用完了怎麼辦?

https://www.modb.pro/db/131380

表的自增 id 達到上限後,再申請時它的值就不會改變,進而導致繼續插入數據時報主鍵衝突的錯誤

索引重建

重建普通索引時,直接先刪除索引,再重新創建即可。

alter table T drop index k; 
alter table T add index(k);

主鍵索引不能通過上面的語句去重建,因為刪除主鍵索引後,innodb會如下處理:

  • 如果存在非空且欄位類型為數值的唯一索引(INT and NOT NULL and UNIQUE INDEX), 會將第一個滿足條件的索引作為主鍵索引, _rowid為對應主鍵,值與唯一索引相同。(可通過select _rowid from table查詢)。
  • 如果找不到合適的索引,那麼 InnoDB 會自動生成一個不可見的名為 ROW_ID 的列名為 GEN_CLUST_INDEX 的主鍵索引,該列是一個 6 位元組的自增數值,隨著插入而自增。

所以刪除主鍵索引的結果其實是修改了主鍵欄位,而普通索引的葉子節點存的是主鍵的值,所以,一旦修改了主鍵欄位,普通索引也會有影響,葉子節點的值將被修改成新的主鍵欄位。

當主鍵索引需要重建時,更好的做法是直接使用alter table t engine=innodb重建表。

05 | 深入淺出索引(下)

image

Q :執行 select * from T where k between 3 and 5,需要執行幾次樹的搜索操作,會掃描多少行?

A:

  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,不滿足條件,迴圈結束。

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

覆蓋索引

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

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

在高頻請求考慮建立聯合索引(例如:(身份證號、姓名)),就是用到了覆蓋索引,不再需要回表查整行記錄,減少語句的執行時間。

最左首碼原則

「聯合索引的多個欄位中,只有當查詢條件為聯合索引的第一個欄位時,查詢才能使用該索引。」

這個最左首碼可以是聯合索引的最左 N 個欄位

「索引可以根據欄位值最左若幹個字元進行模糊查詢。」

分析(name,age)這個聯合索引,可以看到,索引項是按照索引定義裡面出現的欄位順序排序的。當你的邏輯需求是查到所有名字是“張三”的人時,可以快速定位到 ID4,然後向後遍歷得到所有需要的結果。如果你要查的是所有名字第一個字是“張”的人,你的 SQL 語句的條件是"where name like ‘張 %’"。這時,你也能夠用上這個索引,查找到第一個符合條件的記錄是 ID3,然後向後遍歷,直到不滿足條件為止。

image

現在,假設我們有一下三種查詢情景:

  • 查出用戶名的第一個字是 “張” 開頭的人的密碼。即查詢條件子句為 "where username like ' 張 %'"
  • 查處用戶名中含有 “張” 字的人的密碼。即查詢條件子句為 "where username like '% 張 %'"
  • 查出用戶名以 “張” 字結尾的人的密碼。即查詢條件子句為 "where username like '% 張 '"

以上三種情況下,只有第 1 種能夠使用(username,password)聯合索引來加快查詢速度。

Q:表id為主鍵,username為普通索引,問select id, username from user_table where username like '%張%' 能否使用到 (username) 索引?

A:答案是可以的,因為查詢的所有欄位 (id, username) 在二級索引(username)中都存在,二級索引樹比主鍵索引樹小很多,所以會直接遍歷二級索引。值得註意的是,這裡是遍歷整個索引樹,而不是在索引樹中快速定位數據

首碼索引

Q:需要根據 email 欄位查找用戶信息的需求,當然我們可以直接給 email 欄位創建一個索引,但我們仔細想想,有必要為整個 email 欄位創建索引嗎?

A:其實沒必要的,因為郵箱地址是有一個格式的,都是 "[email protected]", 所以其實 email 欄位的後面幾位區分度不高。這時為整個 email 欄位創建索引很浪費空間,我們可以創建首碼索引,將欄位的前幾個字元作為索引即可。

mysql 中使用 ADD KEY (column_name (prefix_length)) 為欄位創建首碼索引。

首碼索引設計的好壞在於選擇合適的首碼索引長度。如果選擇太長,會造成索引空間的浪費;如果選擇太短,會導致索引樹大量重覆的 key,索引效果不理想

問題:

  • 使用首碼索引可能會增加記錄掃描次數與回表次數,影響性能
  • 使用首碼索引就用不上覆蓋索引對查詢性能的優化了

首碼索引的區分度不夠高怎麼辦

一個很常見的解決手段就是 Hash。

對這個超長欄位 a 進行 hash(假設命名為 a_hash) 存入資料庫,然後對這個 hash 值建立索引,由於 hash 值同樣可能存在衝突,也就是說兩個不同的 a 通過 Hash 函數得到的結果可能是相同的,所以我們在查詢語句的 where 部分還需要進行一次精確判斷

索引下推

對於 user_table 表,我們現在有(username,age)聯合索引 如果現在有一個需求,查出名稱中以 “張” 開頭且年齡小於等於 10 的用戶信息,語句 C 如下:

"select * from user_table where username like ' 張 %' and age > 10".

語句 C 會根據(username,age)聯合索引查詢所有滿足名稱以 “張” 開頭的索引,然後直接再篩選出年齡小於等於 10 的索引,之後再回表查詢全行數據

image

第二種方式需要回表查詢的全行數據比較少,這就是 mysql 的索引下推,在索引遍歷過程中,對索引中包含的欄位先做判斷,直接過濾掉不滿足條件的記錄,減少回表次數

https://copyfuture.com/blogs-details/20211116185246587T#_157

https://mp.weixin.qq.com/s?__biz=MzI4MTc0NDg2OQ==&mid=2247483854&idx=1&sn=13a28d8754b2718d8e6a044492897b99


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

-Advertisement-
Play Games
更多相關文章
  • 背景 在生產過程中,由於磁碟空間、保留周期等因素,會對系統、應用等日誌提出要求,要求系統日誌定期進行輪轉、壓縮和刪除,從而減少開銷,而系統自帶的logrotate 則是一個簡單又實用的小工具,下麵著重介紹一下,滿足日常需求。 語法 Usage: logrotate [OPTION...] <conf ...
  • Linux 的基本操作 -許可權 許可權: 文件的屬性: d:表示目錄-:表示文件 l:連接文件 b:設備文件,提供存儲的介面設備 c:設備文件,提供串列的介面設備--鍵盤,滑鼠 r:可讀,查看目錄下有哪些文件或文件夾,查看文件內容 w :可寫,在目錄中新普文件夾/文件 修改/刪除文件a/文件內容移動文 ...
  • less 命令: 查看文件內容 概念 less 與 more 類似,less 可以隨意瀏覽文件,支持翻頁和搜索,支持向上翻頁和向下翻頁。而使用 more 命令瀏覽文件內容時,只能不斷向後翻看。 介紹 用法: less [OPTION]... [FILE]... 常用參數: 常用選項及含義 | Key ...
  • zCommander for Mac是一款功能全面的文件管理軟體,為你提供文件列表視圖雙窗格功能,且每個窗格可以有多個選項卡,您只需使用鍵盤界面即可完成大多數文件操作,因此操作簡單且快速,為你的工作提供了不少方便! 詳情:zCommander for Mac(文件管理軟體) 功能特色 -熟悉的文件列 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 安裝前準備工作 因為Nginx依賴於gcc的編譯環境,所以,需要安裝編譯環境來使Nginx能夠編譯起來 yum install gcc-c++ Nginx的http模塊需要使用pcre來解析正則表達式,需要安裝pcre yum install - ...
  • PDF Reader Pro mac版是一款全面且強大的pdf閱讀器,該軟體支持PDF閱讀,編輯,註釋,創建/填寫表格,轉換,創建,OCR和簽署PDF文件等,滿足您的所有PDF文檔需求。簡便高效,大大提升您的工作效率。 詳情:PDF Reader Pro for mac(全能pdf閱讀器) 軟體特色 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 系統版本:CentOS Linux release 7.6.1810 (Core) docker版本:20.10.12 docker-compose版本:v2.3.2 一、安裝docker-compose: 把在百度網盤下載的docker-com ...
  • Clobbr是一款開發人員必備的工具,使用它可以測試您的api端點,以查看它們在多個請求(破壞您的 api!)下的執行情況,順序或並行,滿足您測試REST、GraphQL、雲函數、Clobbr 的需求。可以輕鬆向多個端點發出請求;輕鬆配置動詞(GET、PUT、POST 等);為每個請求配置標頭;擁有 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...