超详细图解!【MySQL进阶篇】MySQL索引原理
索引類型
索引可以提升查詢速度,會(huì)影響where查詢,以及order by排序。MySQL索引類型如下:
-
從索引存儲(chǔ)結(jié)構(gòu)劃分:B Tree索引、Hash索引、FULLTEXT全文索引、R Tree索引
-
從應(yīng)用層次劃分:普通索引、唯一索引、主鍵索引、復(fù)合索引
-
從索引鍵值類型劃分:主鍵索引、輔助索引(二級索引)
-
從數(shù)據(jù)存儲(chǔ)和索引鍵值邏輯關(guān)系劃分:聚集索引(聚簇索引)、非聚集索引(非聚簇索引)
普通索引
這是最基本的索引類型,基于普通字段建立的索引,沒有任何限制。
創(chuàng)建普通索引的方法如下:
-
CREATE INDEX <索引的名字> ON tablename (字段名);
-
ALTER TABLE tablename ADD INDEX [索引的名字] (字段名);
-
CREATE TABLE tablename ( […], INDEX [索引的名字] (字段名) );
唯一索引
與"普通索引"類似,不同的就是:索引字段的值必須唯一,但允許有空值 。在創(chuàng)建或修改表時(shí)追加唯一
約束,就會(huì)自動(dòng)創(chuàng)建對應(yīng)的唯一索引。
創(chuàng)建唯一索引的方法如下:
-
CREATE UNIQUE INDEX <索引的名字> ON tablename (字段名);
-
ALTER TABLE tablename ADD UNIQUE INDEX [索引的名字] (字段名);
-
CREATE TABLE tablename ( […], UNIQUE [索引的名字] (字段名) ;
主鍵索引
它是一種特殊的唯一索引,不允許有空值。在創(chuàng)建或修改表時(shí)追加主鍵約束即可,每個(gè)表只能有一個(gè)主
鍵。
創(chuàng)建主鍵索引的方法如下:
- CREATE TABLE tablename ( […], PRIMARY KEY (字段名) );
- ALTER TABLE tablename ADD PRIMARY KEY (字段名);
復(fù)合索引
單一索引是指索引列為一列的情況,即新建索引的語句只實(shí)施在一列上;用戶可以在多個(gè)列上建立索
引,這種索引叫做組復(fù)合索引(組合索引)。復(fù)合索引可以代替多個(gè)單一索引,相比多個(gè)單一索引復(fù)合
索引所需的開銷更小。
索引同時(shí)有兩個(gè)概念叫做窄索引和寬索引,窄索引是指索引列為1-2列的索引,寬索引也就是索引列超
過2列的索引,設(shè)計(jì)索引的一個(gè)重要原則就是能用窄索引不用寬索引,因?yàn)檎饕冉M合索引更有
效。
創(chuàng)建組合索引的方法如下:
-
CREATE INDEX <索引的名字> ON tablename (字段名1,字段名2…);
-
ALTER TABLE tablename ADD INDEX [索引的名字] (字段名1,字段名2…);
-
CREATE TABLE tablename ( […], INDEX [索引的名字] (字段名1,字段名2…) );
復(fù)合索引使用注意事項(xiàng):
- 何時(shí)使用復(fù)合索引,要根據(jù)where條件建索引,注意不要過多使用索引,過多使用會(huì)對更新操作效
率有很大影響。 - 如果表已經(jīng)建立了(col1,col2),就沒有必要再單獨(dú)建立(col1);如果現(xiàn)在有(col1)索引,如果查
詢需要col1和col2條件,可以建立(col1,col2)復(fù)合索引,對于查詢有一定提高。
全文索引
查詢操作在數(shù)據(jù)量比較少時(shí),可以使用like模糊查詢,但是對于大量的文本數(shù)據(jù)檢索,效率很低。如果
使用全文索引,查詢速度會(huì)比like快很多倍。在MySQL 5.6 以前的版本,只有MyISAM存儲(chǔ)引擎支持全
文索引,從MySQL 5.6開始MyISAM和InnoDB存儲(chǔ)引擎均支持。
創(chuàng)建全文索引的方法如下:
-
CREATE FULLTEXT INDEX <索引的名字> ON tablename (字段名);
-
ALTER TABLE tablename ADD FULLTEXT [索引的名字] (字段名);
-
CREATE TABLE tablename ( […], FULLTEXT KEY [索引的名字] (字段名) ;
和常用的like模糊查詢不同,全文索引有自己的語法格式,使用 match 和 against 關(guān)鍵字,比如
SQLselect * from user where match(name) against('aaa');全文索引使用注意事項(xiàng):
-
全文索引必須在字符串、文本字段上建立。
-
全文索引字段值必須在最小字符和最大字符之間的才會(huì)有效。(innodb:3-84;myisam:4-
84) -
全文索引字段值要進(jìn)行切詞處理,按syntax字符進(jìn)行切割,例如b+aaa,切分成b和aaa
-
全文索引匹配查詢,默認(rèn)使用的是等值匹配,例如a匹配a,不會(huì)匹配ab,ac。如果想匹配可以在布
爾模式下搜索a*
索引原理
MySQL官方對索引定義:是存儲(chǔ)引擎用于快速查找記錄的一種數(shù)據(jù)結(jié)構(gòu)。需要額外開辟空間和數(shù)據(jù)維護(hù)
工作。
- 索引是物理數(shù)據(jù)頁存儲(chǔ),在數(shù)據(jù)文件中(InnoDB,ibd文件),利用數(shù)據(jù)頁(page)存儲(chǔ)。
- 索引可以加快檢索速度,但是同時(shí)也會(huì)降低增刪改操作速度,索引維護(hù)需要代價(jià)。
索引涉及的理論知識(shí):二分查找法、Hash和B+Tree。
二分查找法
二分查找法也叫作折半查找法,它是在有序數(shù)組中查找指定數(shù)據(jù)的搜索算法。它的優(yōu)點(diǎn)是等值查詢、范
圍查詢性能優(yōu)秀,缺點(diǎn)是更新數(shù)據(jù)、新增數(shù)據(jù)、刪除數(shù)據(jù)維護(hù)成本高。
-
首先定位left和right兩個(gè)指針
-
計(jì)算(left+right)/2
-
判斷除2后索引位置值與目標(biāo)值的大小比對
-
索引位置值大于目標(biāo)值就-1,right移動(dòng);如果小于目標(biāo)值就+1,left移動(dòng)
舉個(gè)例子,下面的有序數(shù)組有17 個(gè)值,查找的目標(biāo)值是7,過程如下:
第一次查找
第二次查找
第三次查找
第四次查找
Hash結(jié)構(gòu)
Hash底層實(shí)現(xiàn)是由Hash表來實(shí)現(xiàn)的,是根據(jù)鍵值 <key,value> 存儲(chǔ)數(shù)據(jù)的結(jié)構(gòu)。非常適合根據(jù)key查找
value值,也就是單個(gè)key查詢,或者說等值查詢。其結(jié)構(gòu)如下所示:
從上面結(jié)構(gòu)可以看出,Hash索引可以方便的提供等值查詢,但是對于范圍查詢就需要全表掃描了。
Hash索引在MySQL 中Hash結(jié)構(gòu)主要應(yīng)用在Memory原生的Hash索引 、InnoDB自適應(yīng)哈希索引。
InnoDB提供的自適應(yīng)哈希索引功能強(qiáng)大,接下來重點(diǎn)描述下InnoDB自適應(yīng)哈希索引。
InnoDB自適應(yīng)哈希索引是為了提升查詢效率,InnoDB存儲(chǔ)引擎會(huì)監(jiān)控表上各個(gè)索引頁的查詢,當(dāng)
InnoDB注意到某些索引值訪問非常頻繁時(shí),會(huì)在內(nèi)存中基于B+Tree索引再創(chuàng)建一個(gè)哈希索引,使得內(nèi)
存中的 B+Tree 索引具備哈希索引的功能,即能夠快速定值訪問頻繁訪問的索引頁。
InnoDB自適應(yīng)哈希索引:在使用Hash索引訪問時(shí),一次性查找就能定位數(shù)據(jù),等值查詢效率要優(yōu)于
B+Tree。
自適應(yīng)哈希索引的建立使得InnoDB存儲(chǔ)引擎能自動(dòng)根據(jù)索引頁訪問的頻率和模式自動(dòng)地為某些熱點(diǎn)頁
建立哈希索引來加速訪問。另外InnoDB自適應(yīng)哈希索引的功能,用戶只能選擇開啟或關(guān)閉功能,無法
進(jìn)行人工干涉。
B+Tree結(jié)構(gòu)
MySQL數(shù)據(jù)庫索引采用的是B+Tree結(jié)構(gòu),在B-Tree結(jié)構(gòu)上做了優(yōu)化改造。
B-Tree結(jié)構(gòu)
-
索引值和data數(shù)據(jù)分布在整棵樹結(jié)構(gòu)中
-
每個(gè)節(jié)點(diǎn)可以存放多個(gè)索引值及對應(yīng)的data數(shù)據(jù)
-
樹節(jié)點(diǎn)中的多個(gè)索引值從左到右升序排列
B樹的搜索:從根節(jié)點(diǎn)開始,對節(jié)點(diǎn)內(nèi)的索引值序列采用二分法查找,如果命中就結(jié)束查找。沒有
命中會(huì)進(jìn)入子節(jié)點(diǎn)重復(fù)查找過程,直到所對應(yīng)的的節(jié)點(diǎn)指針為空,或已經(jīng)是葉子節(jié)點(diǎn)了才結(jié)束。
B+Tree結(jié)構(gòu)
-
非葉子節(jié)點(diǎn)不存儲(chǔ)data數(shù)據(jù),只存儲(chǔ)索引值,這樣便于存儲(chǔ)更多的索引值
-
葉子節(jié)點(diǎn)包含了所有的索引值和data數(shù)據(jù)
-
葉子節(jié)點(diǎn)用指針連接,提高區(qū)間的訪問性能
相比B樹,B+樹進(jìn)行范圍查找時(shí),只需要查找定位兩個(gè)節(jié)點(diǎn)的索引值,然后利用葉子節(jié)點(diǎn)的指針進(jìn)
行遍歷即可。而B樹需要遍歷范圍內(nèi)所有的節(jié)點(diǎn)和數(shù)據(jù),顯然B+Tree效率高。
聚簇索引和輔助索引
簇索引和非聚簇索引:B+Tree的葉子節(jié)點(diǎn)存放主鍵索引值和行記錄就屬于聚簇索引;如果索引值和行
記錄分開存放就屬于非聚簇索引。
主鍵索引和輔助索引:B+Tree的葉子節(jié)點(diǎn)存放的是主鍵字段值就屬于主鍵索引;如果存放的是非主鍵值
就屬于輔助索引(二級索引)。
在InnoDB引擎中,主鍵索引采用的就是聚簇索引結(jié)構(gòu)存儲(chǔ)。
聚簇索引(聚集索引)
聚簇索引是一種數(shù)據(jù)存儲(chǔ)方式,InnoDB的聚簇索引就是按照主鍵順序構(gòu)建 B+Tree結(jié)構(gòu)。B+Tree
的葉子節(jié)點(diǎn)就是行記錄,行記錄和主鍵值緊湊地存儲(chǔ)在一起。 這也意味著 InnoDB 的主鍵索引就
是數(shù)據(jù)表本身,它按主鍵順序存放了整張表的數(shù)據(jù),占用的空間就是整個(gè)表數(shù)據(jù)量的大小。通常說
的主鍵索引就是聚集索引。
InnoDB的表要求必須要有聚簇索引:
-
如果表定義了主鍵,則主鍵索引就是聚簇索引
-
如果表沒有定義主鍵,則第一個(gè)非空unique列作為聚簇索引
-
否則InnoDB會(huì)從建一個(gè)隱藏的row-id作為聚簇索引
輔助索引
InnoDB輔助索引,也叫作二級索引,是根據(jù)索引列構(gòu)建 B+Tree結(jié)構(gòu)。但在 B+Tree 的葉子節(jié)點(diǎn)中
只存了索引列和主鍵的信息。二級索引占用的空間會(huì)比聚簇索引小很多, 通常創(chuàng)建輔助索引就是
為了提升查詢效率。一個(gè)表InnoDB只能創(chuàng)建一個(gè)聚簇索引,但可以創(chuàng)建多個(gè)輔助索引。
非聚簇索引
與InnoDB表存儲(chǔ)不同,MyISAM數(shù)據(jù)表的索引文件和數(shù)據(jù)文件是分開的,被稱為非聚簇索引結(jié)
構(gòu)。
索引分析與優(yōu)化
EXPLAIN
MySQL 提供了一個(gè) EXPLAIN 命令,它可以對 SELECT 語句進(jìn)行分析,并輸出 SELECT 執(zhí)行的詳細(xì)信
息,供開發(fā)人員有針對性的優(yōu)化。例如:
EXPLAIN SELECT * from user WHERE id < 3;
EXPLAIN 命令的輸出內(nèi)容大致如下:
select_type
表示查詢的類型。常用的值如下:
-
SIMPLE : 表示查詢語句不包含子查詢或union
-
PRIMARY:表示此查詢是最外層的查詢
-
UNION:表示此查詢是UNION的第二個(gè)或后續(xù)的查詢
-
EXPLAIN SELECT * from user WHERE id < 3;
-
DEPENDENT UNION:UNION中的第二個(gè)或后續(xù)的查詢語句,使用了外面查詢結(jié)果
-
UNION RESULT:UNION的結(jié)果
-
SUBQUERY:SELECT子查詢語句
-
DEPENDENT SUBQUERY:SELECT子查詢語句依賴外層查詢的結(jié)果。
最常見的查詢類型是SIMPLE,表示我們的查詢沒有子查詢也沒用到UNION查詢。
type
表示存儲(chǔ)引擎查詢數(shù)據(jù)時(shí)采用的方式。比較重要的一個(gè)屬性,通過它可以判斷出查詢是全表掃描還
是基于索引的部分掃描。常用屬性值如下,從上至下效率依次增強(qiáng)。
-
ALL:表示全表掃描,性能最差。
-
index:表示基于索引的全表掃描,先掃描索引再掃描全表數(shù)據(jù)。
-
range:表示使用索引范圍查詢。使用>、>=、<、<=、in等等。
-
ref:表示使用非唯一索引進(jìn)行單值查詢。
-
eq_ref:一般情況下出現(xiàn)在多表join查詢,表示前面表的每一個(gè)記錄,都只能匹配后面表的一
行結(jié)果。 -
const:表示使用主鍵或唯一索引做等值查詢,常量查詢。
-
NULL:表示不用訪問表,速度最快。
possible_keys
表示查詢時(shí)能夠使用到的索引。注意并不一定會(huì)真正使用,顯示的是索引名稱。
key
表示查詢時(shí)真正使用到的索引,顯示的是索引名稱。
rows
MySQL查詢優(yōu)化器會(huì)根據(jù)統(tǒng)計(jì)信息,估算SQL要查詢到結(jié)果需要掃描多少行記錄。原則上rows是
越少效率越高,可以直觀的了解到SQL效率高低。
key_len
表示查詢使用了索引的字節(jié)數(shù)量。可以判斷是否全部使用了組合索引。
key_len的計(jì)算規(guī)則如下:
-
字符串類型
字符串長度跟字符集有關(guān):latin1=1、gbk=2、utf8=3、utf8mb4=4
char(n):n*字符集長度
varchar(n):n * 字符集長度 + 2字節(jié) -
數(shù)值類型
TINYINT:1個(gè)字節(jié)
SMALLINT:2個(gè)字節(jié)
MEDIUMINT:3個(gè)字節(jié)
INT、FLOAT:4個(gè)字節(jié)
BIGINT、DOUBLE:8個(gè)字節(jié) -
時(shí)間類型
DATE:3個(gè)字節(jié)
TIMESTAMP:4個(gè)字節(jié)
DATETIME:8個(gè)字節(jié) -
字段屬性
NULL屬性占用1個(gè)字節(jié),如果一個(gè)字段設(shè)置了NOT NULL,則沒有此項(xiàng)。
Extra
Extra表示很多額外的信息,各種操作會(huì)在Extra提示相關(guān)信息,常見幾種如下:
-
Using where
表示查詢需要通過索引回表查詢數(shù)據(jù)。 -
Using index
表示查詢需要通過索引,索引就可以滿足所需數(shù)據(jù)。 -
Using filesort
表示查詢出來的結(jié)果需要額外排序,數(shù)據(jù)量小在內(nèi)存,大的話在磁盤,因此有Using filesort
建議優(yōu)化。 -
Using temprorary
查詢使用到了臨時(shí)表,一般出現(xiàn)于去重、分組等操作。
回表查詢
在之前介紹過,InnoDB索引有聚簇索引和輔助索引。聚簇索引的葉子節(jié)點(diǎn)存儲(chǔ)行記錄,InnoDB必須要
有,且只有一個(gè)。輔助索引的葉子節(jié)點(diǎn)存儲(chǔ)的是主鍵值和索引字段值,通過輔助索引無法直接定位行記
錄,通常情況下,需要掃碼兩遍索引樹。先通過輔助索引定位主鍵值,然后再通過聚簇索引定位行記
錄,這就叫做回表查詢,它的性能比掃一遍索引樹低。
總結(jié):通過索引查詢主鍵值,然后再去聚簇索引查詢記錄信息
覆蓋索引
在SQL-Server官網(wǎng)的介紹如下:
在MySQL官網(wǎng),類似的說法出現(xiàn)在explain查詢計(jì)劃優(yōu)化章節(jié),即explain的輸出結(jié)果Extra字段為Using
index時(shí),能夠觸發(fā)索引覆蓋。
不管是SQL-Server官網(wǎng),還是MySQL官網(wǎng),都表達(dá)了:**只需要在一棵索引樹上就能獲取SQL所需的所
**有列數(shù)據(jù),無需回表,速度更快,這就叫做索引覆蓋。
實(shí)現(xiàn)索引覆蓋最常見的方法就是:將被查詢的字段,建立到組合索引。
最左前綴原則
復(fù)合索引使用時(shí)遵循最左前綴原則,最左前綴顧名思義,就是最左優(yōu)先,即查詢中使用到最左邊的列,
那么查詢就會(huì)使用到索引,如果從索引的第二列開始查找,索引將失效。
LIKE查詢
面試題:MySQL在使用like模糊查詢時(shí),索引能不能起作用?
回答:MySQL在使用Like模糊查詢時(shí),索引是可以被使用的,只有把%字符寫在后面才會(huì)使用到索引。
NULL查詢
面試題:如果MySQL表的某一列含有NULL值,那么包含該列的索引是否有效?
對MySQL來說,NULL是一個(gè)特殊的值,從概念上講,NULL意味著“一個(gè)未知值”,它的處理方式與其他
值有些不同。比如:不能使用=,<,>這樣的運(yùn)算符,對NULL做算術(shù)運(yùn)算的結(jié)果都是NULL,count時(shí)
不會(huì)包括NULL行等,NULL比空字符串需要更多的存儲(chǔ)空間等。
“NULL columns require additional space in the row to record whether their values
are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to
the nearest byte.”
NULL列需要增加額外空間來記錄其值是否為NULL。對于MyISAM表,每一個(gè)空列額外占用一位,四舍
五入到最接近的字節(jié)。
雖然MySQL可以在含有NULL的列上使用索引,但NULL和其他數(shù)據(jù)還是有區(qū)別的,不建議列上允許為
NULL。最好設(shè)置NOT NULL,并給一個(gè)默認(rèn)值,比如0和 ‘’ 空字符串等,如果是datetime類型,也可以
設(shè)置系統(tǒng)當(dāng)前時(shí)間或某個(gè)固定的特殊值,例如’1970-01-01 00:00:00’。
索引與排序
MySQL查詢支持filesort和index兩種方式的排序,filesort是先把結(jié)果查出,然后在緩存或磁盤進(jìn)行排序
操作,效率較低。使用index是指利用索引自動(dòng)實(shí)現(xiàn)排序,不需另做排序操作,效率會(huì)比較高。
filesort有兩種排序算法:雙路排序和單路排序。
雙路排序:需要兩次磁盤掃描讀取,最終得到用戶數(shù)據(jù)。第一次將排序字段讀取出來,然后排序;第二
次去讀取其他字段數(shù)據(jù)。
單路排序:從磁盤查詢所需的所有列數(shù)據(jù),然后在內(nèi)存排序?qū)⒔Y(jié)果返回。如果查詢數(shù)據(jù)超出緩存
sort_buffer,會(huì)導(dǎo)致多次磁盤讀取操作,并創(chuàng)建臨時(shí)表,最后產(chǎn)生了多次IO,反而會(huì)增加負(fù)擔(dān)。解決方
案:少使用select *;增加sort_buffer_size容量和max_length_for_sort_data容量。
如果我們Explain分析SQL,結(jié)果中Extra屬性顯示Using filesort,表示使用了filesort排序方式,需要優(yōu)
化。如果Extra屬性顯示Using index時(shí),表示覆蓋索引,也表示所有操作在索引上完成,也可以使用
index排序方式,建議大家盡可能采用覆蓋索引。
-
以下幾種情況,會(huì)使用index方式的排序。
-
ORDER BY 子句索引列組合滿足索引最左前列
explain select id from user order by id; //對應(yīng)(id)、(id,name)索引有效 -
WHERE子句+ORDER BY子句索引列組合滿足索引最左前列
explain select id from user where age=18 order by name; //對應(yīng)(age,name)索引 -
以下幾種情況,會(huì)使用filesort方式的排序。
-
對索引列同時(shí)使用了ASC和DESC
explain select id from user order by age asc,name desc; //對應(yīng)(age,name)索引 -
WHERE子句和ORDER BY子句滿足最左前綴,但where子句使用了范圍查詢(例如>、<、in
等)
explain select id from user where age>10 order by name; //對應(yīng)(age,name)索引 -
ORDER BY或者WHERE+ORDER BY索引列沒有滿足索引最左前列
explain select id from user order by name; //對應(yīng)(age,name)索引 -
使用了不同的索引,MySQL每次只采用一個(gè)索引,ORDER BY涉及了兩個(gè)索引
explain select id from user order by name,age; //對應(yīng)(name)、(age)兩個(gè)索引 -
WHERE子句與ORDER BY子句,使用了不同的索引
explain select id from user where name='tom' order by age; //對應(yīng)(name)、(age)索引 -
WHERE子句或者ORDER BY子句中索引列使用了表達(dá)式,包括函數(shù)表達(dá)式
explain select id from user order by abs(age); //對應(yīng)(age)索引
查詢優(yōu)化
慢查詢定位
開啟慢查詢?nèi)罩?/strong>
查看 MySQL 數(shù)據(jù)庫是否開啟了慢查詢?nèi)罩竞吐樵內(nèi)罩疚募拇鎯?chǔ)位置的命令如下:
SHOW VARIABLES LIKE 'slow_query_log%'
通過如下命令開啟慢查詢?nèi)罩?#xff1a;
- long_query_time:指定慢查詢的閥值,單位秒。如果SQL執(zhí)行時(shí)間超過閥值,就屬于慢查詢
記錄到日志文件中。 - log_queries_not_using_indexes:表示會(huì)記錄沒有使用索引的查詢SQL。前提是slow_query_log
的值為ON,否則不會(huì)奏效。
查看慢查詢?nèi)罩?/strong>
文本方式查看
直接使用文本編輯器打開slow.log日志即可。
-
time:日志記錄的時(shí)間
-
User@Host:執(zhí)行的用戶及主機(jī)
-
Query_time:執(zhí)行的時(shí)間
-
Lock_time:鎖表時(shí)間
-
Rows_sent:發(fā)送給請求方的記錄數(shù),結(jié)果數(shù)量
-
Rows_examined:語句掃描的記錄條數(shù)
-
SET timestamp:語句執(zhí)行的時(shí)間點(diǎn)
-
select…:執(zhí)行的具體的SQL語句
使用mysqldumpslow查看
MySQL 提供了一個(gè)慢查詢?nèi)罩痉治龉ぞ適ysqldumpslow,可以通過該工具分析慢查詢?nèi)罩?br /> 內(nèi)容。
在 MySQL bin目錄下執(zhí)行下面命令可以查看該使用格式。
perl mysqldumpslow.pl --help
運(yùn)行如下命令查看慢查詢?nèi)罩拘畔?#xff1a;
perl mysqldumpslow.pl -t 5 -s at C:\ProgramData\MySQL\Data\OAK-slow.log
除了使用mysqldumpslow工具,也可以使用第三方分析工具,比如pt-query-digest、
mysqlsla等。
慢查詢優(yōu)化
索引和慢查詢
-
如何判斷是否為慢查詢?
MySQL判斷一條語句是否為慢查詢語句,主要依據(jù)SQL語句的執(zhí)行時(shí)間,它把當(dāng)前語句的執(zhí)
行時(shí)間跟 long_query_time 參數(shù)做比較,如果語句的執(zhí)行時(shí)間 > long_query_time,就會(huì)把
這條執(zhí)行語句記錄到慢查詢?nèi)罩纠锩妗ong_query_time 參數(shù)的默認(rèn)值是 10s,該參數(shù)值可
以根據(jù)自己的業(yè)務(wù)需要進(jìn)行調(diào)整。 -
如何判斷是否應(yīng)用了索引?
SQL語句是否使用了索引,可根據(jù)SQL語句執(zhí)行過程中有沒有用到表的索引,可通過 explain
命令分析查看,檢查結(jié)果中的 key 值,是否為NULL。 -
應(yīng)用了索引是否一定快?
下面我們來看看下面語句的 explain 的結(jié)果,你覺得這條語句有用上索引嗎?比如
select * from user where id>0;
雖然使用了索引,但是還是從主鍵索引的最左邊的葉節(jié)點(diǎn)開始向右掃描整個(gè)索引樹,進(jìn)行了
全表掃描,此時(shí)索引就失去了意義。
而像select * from user where id = 2;這樣的語句,才是我們平時(shí)說的使用了索引。它表示
的意思是,我們使用了索引的快速搜索功能,并且有效地減少了掃描行數(shù)。
查詢是否使用索引,只是表示一個(gè)SQL語句的執(zhí)行過程;而是否為慢查詢,是由它執(zhí)行的時(shí)間決定
的,也就是說是否使用了索引和是否是慢查詢兩者之間沒有必然的聯(lián)系。
我們在使用索引時(shí),不要只關(guān)注是否起作用,應(yīng)該關(guān)心索引是否減少了查詢掃描的數(shù)據(jù)行數(shù),如果
掃描行數(shù)減少了,效率才會(huì)得到提升。對于一個(gè)大表,不止要?jiǎng)?chuàng)建索引,還要考慮索引過濾性,過
濾性好,執(zhí)行速度才會(huì)快。
提高索引過濾性
假如有一個(gè)5000萬記錄的用戶表,通過sex='男’索引過濾后,還需要定位3000萬,SQL執(zhí)行速度也
不會(huì)很快。其實(shí)這個(gè)問題涉及到索引的過濾性,比如1萬條記錄利用索引過濾后定位10條、100
條、1000條,那他們過濾性是不同的。索引過濾性與索引字段、表的數(shù)據(jù)量、表設(shè)計(jì)結(jié)構(gòu)都有關(guān)
系。
- 下面我們看一個(gè)案例:
-
優(yōu)化1
alter table student add index(name); //追加name索引 -
優(yōu)化2
`alter table student add index(age,name); //追加age,name索引優(yōu)化3
可以看到,index condition pushdown 優(yōu)化的效果還是很不錯(cuò)的。再進(jìn)一步優(yōu)化,我們可以把名
字的第一個(gè)字和年齡做一個(gè)聯(lián)合索引,這里可以使用 MySQL 5.7 引入的虛擬列來實(shí)現(xiàn)。
慢查詢原因總結(jié)
-
全表掃描:explain分析type屬性all
-
全索引掃描:explain分析type屬性index
-
索引過濾性不好:靠索引字段選型、數(shù)據(jù)量和狀態(tài)、表設(shè)計(jì)
-
頻繁的回表查詢開銷:盡量少用select *,使用覆蓋索引
分頁查詢優(yōu)化
一般性分頁
一般的分頁查詢使用簡單的 limit 子句就可以實(shí)現(xiàn)。limit格式如下:
SELECT * FROM 表名 LIMIT [offset,] rows
-
第一個(gè)參數(shù)指定第一個(gè)返回記錄行的偏移量,注意從0開始;
-
第二個(gè)參數(shù)指定返回記錄行的最大數(shù)目;
-
如果只給定一個(gè)參數(shù),它表示返回最大的記錄行數(shù)目;
思考1:如果偏移量固定,返回記錄量對執(zhí)行時(shí)間有什么影響?
`select * from user limit 10000,1; select * from user limit 10000,10; select * from user limit 10000,100; select * from user limit 10000,1000; select * from user limit 10000,10000**結(jié)果:**在查詢記錄時(shí),返回記錄量低于100條,查詢時(shí)間基本沒有變化,差距不大。隨著查詢記錄
量越大,所花費(fèi)的時(shí)間也會(huì)越來越多。
思考2:如果查詢偏移量變化,返回記錄數(shù)固定對執(zhí)行時(shí)間有什么影響?
結(jié)果:在查詢記錄時(shí),如果查詢記錄量相同,偏移量超過100后就開始隨著偏移量增大,查詢時(shí)間
急劇的增加。(這種分頁查詢機(jī)制,每次都會(huì)從數(shù)據(jù)庫第一條記錄開始掃描,越往后查詢越慢,而
且查詢的數(shù)據(jù)越多,也會(huì)拖慢總查詢速度。)
分頁優(yōu)化方案
第一步:利用覆蓋索引優(yōu)化
第二步:利用子查詢優(yōu)化
SQLselect * from user limit 10000,100; select * from user where id>= (select id from user limit 10000,1) limit 100;原因:使用了id做主鍵比較(id>=),并且子查詢使用了覆蓋索引進(jìn)行優(yōu)化。
最后,祝大家早日學(xué)有所成,拿到滿意offer
總結(jié)
以上是生活随笔為你收集整理的超详细图解!【MySQL进阶篇】MySQL索引原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 京东金条结清多久征信显清空?
- 下一篇: 超详细图解!【MySQL进阶篇】SQL优