免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
MySQL 什么是索引?

該文為《 MySQL 實(shí)戰(zhàn) 45 講》的學(xué)習(xí)筆記,感謝查看,如有錯(cuò)誤,歡迎指正

一、索引簡介

索引就類似書本的目錄,作用就是方便我們更加快速的查找到想要的數(shù)據(jù)。

索引的實(shí)現(xiàn)方式比較多,常見的有哈希表,有序數(shù)組,搜索樹。

1.1 哈希表

哈希表是將數(shù)據(jù)以key-value的形式存儲(chǔ)起來,簡單來說就是將key通過哈希函數(shù)換算成數(shù)組中的一個(gè)確定的位置,將value存到這個(gè)位置去。當(dāng)key比較多時(shí),有可能換算出相同的位置,此時(shí)可以通過鏈表來解決。在查詢時(shí)先找到位置,再對該位置的多個(gè)value進(jìn)行遍歷。

哈希表適合用于等值查詢,由于是無序的,不適合用來做區(qū)間查詢。

1.2 有序數(shù)組

有序數(shù)組在等值查詢和區(qū)間查詢上效率都很高。由于是有序的,可以通過二分法快速得到結(jié)果。也支持范圍查詢。但是也有一個(gè)缺點(diǎn),如果要在中間插入一個(gè)數(shù)據(jù),那么后面的所有記錄都要向后挪一位,成本太高了。

因此,有序數(shù)組只適用于靜態(tài)存儲(chǔ)引擎。 例如我們要保存2019年的出生人口信息,就適合用有序數(shù)組。

1.3 搜索樹

常見的搜索樹有二叉,也有多叉。

二叉樹的特點(diǎn)是:

  • 每個(gè)節(jié)點(diǎn)的左兒子小于父節(jié)點(diǎn),父節(jié)點(diǎn)又小于右兒子。

多叉樹的特點(diǎn)是:

  • 每個(gè)節(jié)點(diǎn)有多個(gè)兒子,兒子之間的大小保證從左到右遞增。

由于索引不止存在內(nèi)存中,還會(huì)寫到磁盤上,而讀磁盤越多,查詢效率越慢。要降低讀磁盤的次數(shù)的話,就要盡量訪問盡量少的數(shù)據(jù)塊。

假設(shè)數(shù)據(jù)塊大小是N,樹高為M,最多可以存的數(shù)據(jù)行數(shù)為 N^(M-1)NM-1 次方)。最多訪問磁盤數(shù)為 M-1。

要使樹高比較小,訪問次數(shù)就少,N叉樹的樹高就小于二叉樹。以 InnoDB 的一個(gè)整數(shù)字段索引為例,這個(gè) N 差不多是 1200,這棵樹高是 4 的時(shí)候,就可以存 1200 的 3 次方個(gè)值,這已經(jīng) 17 億行記錄了。一個(gè) 10 億行的表上一個(gè)整數(shù)字段的索引,查找一個(gè)值最多只需要訪問 3 次磁盤。

數(shù)據(jù)庫底層存儲(chǔ)的核心就是基于這些數(shù)據(jù)模型的。每碰到一個(gè)新數(shù)據(jù)庫,我們需要先關(guān)注它的數(shù)據(jù)模型,這樣才能從理論上分析出這個(gè)數(shù)據(jù)庫的適用場景。


二、InnoDB 的索引模型
  • 在 InnoDB 中,表都是根據(jù)主鍵順序以索引的形式存放的,這種存儲(chǔ)方式的表稱為索引組織表。
  • InnoDB 使用了 B+ 樹索引模型,所以數(shù)據(jù)都是存儲(chǔ)在 B+ 樹中的。

因此,每一個(gè)索引在 InnoDB 里面對應(yīng)一棵 B+ 樹。

2.1 索引分類

根據(jù)字段約束,分為主鍵索引普通索引;根據(jù)字段內(nèi)容是否可重復(fù),分為唯一索引非唯一索引

  • 主鍵索引
    主鍵是一種約束,一個(gè)表中只能有一個(gè)主鍵;
    主鍵可以是多個(gè)列;
    主鍵可以被其它表引用為外鍵使用;
    主鍵索引可以理解為非空字段+唯一索引;
    主鍵索引的葉子節(jié)點(diǎn)存的是整行數(shù)據(jù)。

  • 普通索引(二級(jí)索引)
    一個(gè)表中可以有多個(gè)普通索引;索引可以有多列;
    普通索引的葉子節(jié)點(diǎn)內(nèi)容是主鍵的值;

  • 唯一索引
    字段內(nèi)容不能重復(fù),但是可以為空;
    一個(gè)表中可以有多個(gè)唯一索引;
    不能做外鍵使用;

  • 非唯一索引
    字段內(nèi)容允許重復(fù);

下面以表為例,建表語句:

mysql> 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),兩棵樹的示例示意圖如下:


id字段為主鍵索引,主鍵索引的字段是不會(huì)重復(fù)的,必定是唯一索引;
k字段為普通索引,k的值允許重復(fù),因此是非唯一索引。


2.2 回表操作

分析下面 2 條 SQL 語句:

  1. select * from T where ID=500。此時(shí)用到的是主鍵索引,因此直接從索引中返回了整行記錄,只需要搜索ID這棵 B+ 樹。
  2. select * from T where k=5。此時(shí)用到的是普通索引,需要先搜索 k索引樹,得到ID = 500 ,再根據(jù)500ID索引樹搜索一次。這種需要返回主鍵索引樹搜索的過程,叫做回表。

以上兩條 SQL 語句返回的結(jié)果是一樣的,但是效率卻不一樣,因?yàn)榈?2 條 SQL 語句有一次回表操作,效率會(huì)慢很多,因此,要盡量避免回表操作,多使用主鍵查詢

2.3 頁的分裂與合并

還是以上表為例,如果我們要插入一個(gè)數(shù)據(jù),ID 值為 700,則只需要在 R5 后面新增加 1 條記錄即可。如果插入的值 ID 為 400,那就需要邏輯上挪動(dòng)后面的數(shù)據(jù),空出位置。

如果恰好 R5 所在的數(shù)據(jù)頁已經(jīng)滿了,那么就需要申請一個(gè)新的數(shù)據(jù)頁,并且將 R5 挪過去,這個(gè)情況就叫做頁分裂

數(shù)據(jù)頁中并不是要利用率達(dá)到 100% 才會(huì)申請新的數(shù)據(jù)頁。也不是說只要有數(shù)據(jù)刪除,那么后一頁的數(shù)據(jù)就會(huì)順補(bǔ)到前一頁,這樣太浪費(fèi)性能了。數(shù)據(jù)頁有一個(gè)利用率,假設(shè)分裂是80%,合并是 50%。只要利用率達(dá)到了 80%,就會(huì)申請一個(gè)新的數(shù)據(jù)頁。如果刪除數(shù)據(jù)比較多,利用率低于 50% 了,就會(huì)把后一頁的數(shù)據(jù)合并過來。

如何避免頁分裂造成的性能消耗?常見做法是在表中,設(shè)置一個(gè)自增長的 id 主鍵,這個(gè)字段不能和業(yè)務(wù)相關(guān)。自增主鍵的定義:NOT NULL PRIMARY KEY AUTO_INCREMENT。

這樣每次插入數(shù)據(jù),如果不指定 id 值,就會(huì)自增長到最后,因?yàn)楹蜆I(yè)務(wù)無關(guān),所以沒必要去指定 id 值。這樣可以避免出現(xiàn)頁分裂。


三、索引的一些特點(diǎn)
3.1 覆蓋索引

還是以上表為例,執(zhí)行以下 SQL 語句,分析執(zhí)行過程:

mysql> select * from T where k between 3 and 5;
  1. 在普通索引k上遍歷,得到k=3對應(yīng)的 ID300
  2. 通過 ID=300 去主鍵索引上取得整行記錄R3;
  3. 繼續(xù)向后遍歷k,得到k=5對應(yīng)的 ID500;
  4. 通過 ID=500 去主鍵索引上取得整行記錄R5
  5. 繼續(xù)向后遍歷k,發(fā)現(xiàn)k=6,不滿足between條件,循環(huán)結(jié)束。

可以看到,這個(gè)過程讀了k索引樹的 3 條記錄(步驟 1,3,5), 回表了2次(步驟2,4)。

如果我們換成以下 SQL 語句:

mysql> select ID from T where k between 3 and 5;

由于 ID已經(jīng)在k索引樹上了,因此可以直接返回結(jié)果,不用回表。這種索引中已經(jīng)覆蓋了我們要查詢的數(shù)據(jù),叫做覆蓋索引。

覆蓋索引可以減少樹的搜索次數(shù)(沒有回表過程),顯著提高查詢性能。

3.2 關(guān)于掃描行數(shù)

MySQL 認(rèn)為上述操作掃描的行數(shù)是 2 行,因?yàn)樵谒饕胁閿?shù)據(jù),是在引擎層的操作。而 Server 層最后只拿到了 2 條記錄,因此 MySQL 認(rèn)為只掃描了 2 行。

那么如何看掃描函數(shù)呢?有 2 種方法:

  1. 使用explain查看預(yù)計(jì)掃描行數(shù)
mysql> explain select * from t where a between 1000 and 2000;
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
|  1 | SIMPLE      | t     | range | a             | a    | 5       | NULL | 1000 | Using index condition |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
1 row in set (0.01 sec)

mysql>

可以看到使用了索引 key=a,預(yù)計(jì)掃描行數(shù)rows=1000

  1. 將慢日志記錄時(shí)間設(shè)置為 0 ,直接在慢日志中查看掃描行數(shù)
# Time: 191228 13:03:16
# User@Host: federated[federated] @  [60.191.76.22]  Id:   177
# Query_time: 31.211439  Lock_time: 0.000059 Rows_sent: 0  Rows_examined: 95324
SET timestamp=1577509396;
CALL Z10004();

可以看到,掃描行數(shù)為Rows_examined: 95324

3.3 最左前綴原則

舉一個(gè)例子來理解最左前綴原則,假設(shè)有一個(gè)聯(lián)合索引(name,age)如下:


可以看到,索引順序先按照第一個(gè)字段排序,再按照第二個(gè)字段。

假設(shè)我們要查詢所有名為張三的數(shù)據(jù)??梢钥焖俣ㄎ坏?code>ID4,再依次向后遍歷。如果要查詢所有姓張(where name like '張%'),也能用到索引,先定位到ID3,再依次向后遍歷,直到不滿足條件為止。

不只是索引的全部定義,只要滿足最左前綴,就可以利用索引來加速檢索。這個(gè)最左前綴可以是聯(lián)合索引的最左 N 個(gè)字段,也可以是字符串索引的最左 M 個(gè)字符。

在建立聯(lián)合索引時(shí),如何確定字段的前后順序呢?

  • 第一原則,如果通過調(diào)整順序,可以少維護(hù)一個(gè)索引,那么這個(gè)順序往往就是需要優(yōu)先考慮采用的。
    比如,已經(jīng)有了一個(gè)(a, b)索引,就不必再建立一個(gè) a 索引了。

  • 考慮磁盤空間占用大小。
    比如,(name, age) 索引加上 age 索引,和 (age, name) 索引加上 name 索引。這兩種情況,我們就要考慮占用空間了。選擇占用空間小的。
    由于name 字段比 age 字段大,因此我們選擇(name, age) 索引加上 age 索引。

3.4 索引下推

索引下推功能是在 MySQL 5.6 引入的,目的是減少回表次數(shù)。

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

mysql> select * from tuser where name like '張%' and age=10 and ismale=1;
  • 沒有索引下推
    先定位到ID3,然后回表到主鍵索引,找出對應(yīng)的數(shù)據(jù)行,判斷是否符合and age=10 and ismale=1。最終要回表 4 次(ID3,ID4,ID5,ID6),返回的結(jié)果只有 ID4,ID5。
  • 索引下推
    在回表之前,會(huì)先判斷這個(gè)聯(lián)合索引上的后續(xù)字段是否滿足條件,不滿足則不進(jìn)行回表操作。最終只用回表 2 次。



本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
04.深入淺出索引(上)undefined
新來的架構(gòu)師對我說,“你怎么用count(*),太慢了,用count(1)”
百度面試題匯編
索引優(yōu)化案例
再有人問你MySQL索引原理,就把這篇文章甩給他!
MySQL 的索引是怎么組織的?
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服