1. 程式人生 > 其它 >MySQL 索引使用什麼資料結構?為什麼用 B + 做索引?

MySQL 索引使用什麼資料結構?為什麼用 B + 做索引?

使用 B + 樹

這個問題,你可以在腦子裡面先思考一下,如果讓你來設計資料庫的索引,你會怎麼設計?

我們還是用 Why?What?How?三步法來看這個問題。

為什麼會需要索引?索引是什麼?索引怎麼用的?

再思考為什麼需要 B + 樹?B + 樹是什麼?B + 樹怎麼用?

答:大部分程式主要的功能都是對資料的處理,寫入、查詢、轉化、輸出。最形象的比喻就是樹和內容和目錄的關係,目錄就是索引,我們根據目錄能快速拿到想要內容的頁碼。


為什麼是 B + 樹,有這個幾個理由:

如果是用 AVL 平衡二叉樹,樹高度太高,索引查詢需要訪問磁碟,每次訪問以節點為單位進行磁碟 I/O ,需要儘量減少資料讀取的 I/O 操作,所以樹高度一定不能太高,儲存千萬級別的資料,實踐中 B+ 樹的高度也就 4 或者 5。
B + 樹經常用來比較的是 B 樹,B + 樹相比 B 樹有個很大的特點是 B + 樹所有關鍵字都出現在葉子結點的連結串列中(稠密索引),且連結串列中的關鍵字恰好是有序的,對於範圍查詢,比如 15~50,B 樹需要中序遍歷二叉樹,但是 B + 樹直接在葉子節點順序訪問就可以了。
什麼是最左匹配原則?
首先說明一點:

最左字首匹配原則:在 MySQL 建立聯合索引時會遵守最左字首匹配原則,即最左優先,在檢索資料時從聯合索引的最左邊開始匹配。

打個比方,我們有張 student 表,我們根據學院編號 + 班級建立了一個聯合索引 index_magor_class (magor,class), 這個索引由二個欄位組成。

索引的底層是一顆 B + 樹,那麼聯合索引的底層也就是一顆 B + 樹,只不過聯合索引的 B + 樹節點中儲存的是逗號分隔的多個值。

舉例:建立一個 index_magor_class (magor,class) 的聯合索引,那麼它的索引樹就是下圖的樣子。

它是先根據 magor 排序,再根據 class 排序,如果索引後面還有欄位,繼續以此類推。


我們查詢的 where 條件如果只傳入了班級,是走不到聯合索引的,但是如果只傳了學院編號,是可能會走到聯合索引的。(為什麼說可能,MYSQL 的執行計劃和查詢的實際執行過程並不完全吻合,比如你資料庫資料量很少,可能直接全量遍歷速度更快,就不走索引了)

在建表的時候如何設計索引的?有沒有做過索引優化 ?
利用覆蓋索引來進行查詢操作,來避免回表操作。
說明:如果一本書需要知道第 11 章是什麼標題,會翻開第 11 章對應的那一頁嗎?目錄瀏覽一下就好,這個目錄就是起到覆蓋索引的作用。

什麼意思,比如你主鍵索引是學號,你寫 select 語句的時候,直接 select 學號 from table 就可以了,不用 select 其他欄位,一般除非非常有必要,儘量按需 select 欄位,少用或不用 **select ***, 不然還需要回表。

這裡我解釋一下回表,比如我們表主鍵索引是學號,另外我們還根據手機號也建了索引,如果我們 where 條件是手機號,分二種情況:

正例:IDB 能夠建立索引的種類分為【主鍵索引、唯一索引、普通索引】,而覆蓋索引是一種查詢的一種效果,用 explain 的結果,extra 列會出現:using index.

如果我們 select 獲取的欄位是學號,直接在手機號的索引表就能獲取到資料,不需要回表;
如果我們 select 的時候還有其他欄位,我們查詢的時候流程是這樣的,先根據手機號查到學號,再根據學號去主鍵索引表查詢資料,這個過程叫回表。
業務上具有唯一特性的欄位,即使是組合欄位,也建議建成唯一索引。說明:不要以為唯一索引影響了 insert 速度,這個速度損耗可以忽略,但提高查詢速度是明顯的;另外,即使在應用層做了非常完善的校驗和控制,只要沒有唯一索引,根據墨菲定律,必然有髒資料產生。
超過三個表禁止 join。需要 join 的欄位,資料型別保持絕對一致;多表關聯查詢時,保證被關聯的欄位需要有索引。說明:即使雙表 join 也要注意表索引、SQL 效能。
在 varchar 欄位上建立索引時,必須指定索引長度,沒必要對全欄位建立索引,根據實際文字區分度決定索引長度。說明:索引的長度與區分度是一對矛盾體,一般對字串型別資料,長度為 20 的索引,區分度會高達 90% 以上,可以使用 count (distinct left (列名,索引長度))/count (*) 的區分度來確定。
頁面搜尋嚴禁左模糊或者全模糊,如果需要請走搜尋引擎來解決。說明:索引檔案具有 B-Tree 的最左字首匹配特性,如果左邊的值未確定,那麼無法使用此索引。
SQL 效能優化的目標:至少要達到 range 級別,要求是 ref 級別,如果可以是 const 最好。說明:
const 單表中最多隻有一個匹配行(主鍵或者唯一索引),在優化階段即可讀取到資料。
ref 指的是使用普通的索引。(normal index)
range 對索引進行範圍檢索。反例:explain 表的結果,type=index,索引物理檔案全掃描,速度非常慢,這個 index 級別比較 range 還低,與全表掃描是小巫見大巫。
建組合索引的時候,區分度最高的在最左邊。正例:如果 where a=? and b=? ,a 列的幾乎接近於唯一值,那麼只需要單建 idx_a 索引即可。說明:存在非等號和等號混合判斷條件時,在建索引時,請把等號條件的列前置。如:where c>? and d=? 那麼即使 c 的區分度更高,也必須把 d 放在索引的最前列,即建立組合索引 idx_d_c。
防止因欄位型別不同造成的隱式轉換,導致索引失效。
MyBatis 用過嗎?一二級快取清楚嗎?
一級快取 Mybatis 的一級快取是指 SQLSession,一級快取的作用域是 SQlSession, Mabits 預設開啟一級快取。在同一個 SqlSession 中,執行相同的 SQL 查詢時;第一次會去查詢資料庫,並寫在快取中,第二次會直接從快取中取。當執行 SQL 時候兩次查詢中間發生了增刪改的操作,則 SQLSession 的快取會被清空。每次查詢會先去快取中找,如果找不到,再去資料庫查詢,然後把結果寫到快取中。Mybatis 的內部快取使用一個 HashMap,key 為 hashcode+statementId+sql 語句。Value 為查詢出來的結果集對映成的 java 物件。SqlSession 執行 insert、update、delete 等操作 commit 後會清空該 SQLSession 快取。
二級快取 二級快取是 mapper 級別的,Mybatis 預設是沒有開啟二級快取的。第一次呼叫 mapper 下的 SQL 去查詢使用者的資訊,查詢到的資訊會存放在該 mapper 對應的二級快取區域。第二次呼叫 namespace 下的 mapper 對映檔案中,相同的 sql 去查詢使用者資訊,會去對應的二級快取內取結果。

MySQL 主從同步怎麼做的?binlog 清楚嗎?
Master 資料庫只要發生變化,立馬記錄到 Binary log 日誌檔案中
Slave 資料庫啟動一個 I/O thread 連線 Master 資料庫,請求 Master 變化的二進位制日誌
Slave I/O 獲取到的二進位制日誌,儲存到自己的 Relay log 日誌檔案中。
Slave 有一個 SQL thread 定時檢查 Realy log 是否變化,變化那麼就更新資料

MySQL 有沒有做分庫分表?怎麼設計的?
Why?:

當一張表的資料達到幾千萬時,你查詢一次所花的時間會變多,如果有聯合查詢的話,我想有可能會死在那兒了。分表的目的就在於此,減小資料庫的負擔,縮短查詢時間。

mysql 中有一種機制是表鎖定和行鎖定,是為了保證資料的完整性。表鎖定表示你們都不能對這張表進行操作,必須等我對錶操作完才行。行鎖定也一樣,別的 sql 必須等我對這條資料操作完了,才能對這條資料進行操作。

When?(什麼時候需要分表?):

單錶行數超過 500 萬行或者單表容量超過 2GB,才推薦進行分庫分表。說明:如果預計三年後的資料量根本達不到這個級別,請不要在建立表時就分庫分表。反例:某業務三年總資料量才 2 萬行,卻分成 1024 張表,問:你為什麼這麼設計?答:分 1024 張表,不是標配嗎?

How?(分庫分表有幾種策略):

垂直拆分原則:

水平拆分原則:

拆分中介軟體:

詳細可以參考這篇:基於 Mysql 資料庫億級資料下的分庫分表方案

Sharding-sphere,前身是 sharding-jdbc;噹噹的分庫分表中介軟體
TDDL:jar,Taobao Distribute Data Layer;
Mycat:中介軟體。注:工具的利弊,請自行調研,官網和社群優先。
按照 userId 緯度拆分,安琪拉見過的常見的有,根據 userId % 64 取模拆 0~63 編號的 64 張表,
固定位拆,取 userId 指定二位,例如倒數 2,3 位組成 00~99 一共 100 張表的,百庫表表。
hash: userId hash 一下,然後 % 表數;
Range: 另外還有按照 userId 指定範圍拆的,0-1 千萬一張表,這種用的比較少,容易產生熱點。
把不同業務域的表拆成不同庫,例如訂單相關表、使用者資訊相關表、營銷相關表分開在不同庫;
把大欄位獨立儲存到一張表中
把不常用的欄位單獨拿出來儲存到一張表
用 userId 做的分庫分表,現在需要用電話號碼查詢怎麼辦?
和回表邏輯一樣,單獨建一個電話號碼索引表,存放電話號碼和 userId,查詢時先根據電話號碼查詢 userId,然後再根據 userId 查詢資料。
————————————————
版權宣告:本文為CSDN博主「一代梟雄_宗某」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/m0_60566325/article/details/125051026