MySQL查詢優(yōu)化——使用索引和SQL優(yōu)化

如何提高M(jìn)ySQL數(shù)據(jù)庫的查詢效率懒浮,可以從兩個(gè)方面入手:使用索引和使用JOIN梭伐,本文主要講使用索引的一些原則和優(yōu)化方法读整。以及如何設(shè)計(jì)數(shù)據(jù)庫和SQL語句狂秘,來避免一些會(huì)導(dǎo)致性能差的操作。

關(guān)于索引的原理層面的東西假残,本文暫不細(xì)講缭贡。

本篇包括:

  • 索引類型
  • 使用索引的原則
  • 索引優(yōu)化的方式
  • SQL優(yōu)化方式
  • 單表的使用優(yōu)化



索引類型

在MySQL中,索引分為量大類型:聚簇索引非聚簇索引辉懒。
聚簇索引是按照數(shù)據(jù)存放的物理位置為順序的阳惹,而非聚簇索引則不同;聚簇索引可以提高多行的檢索速度眶俩,而非聚簇索引對單行的檢索速度很快穆端。
(PS:聚簇和非聚簇索引的原理部分也很重要,可以去參考其他文章)

在這兩大索引類型下仿便,又細(xì)分為四個(gè)類型
1)普通索引:最基本的索引体啰,沒有任何限制,我們大多數(shù)情況下使用到的索引嗽仪。
2)唯一索引:與普通索引不同的是荒勇,唯一索引的列值必須唯一,但是允許為空值闻坚。
3)全文索引:僅適用于MyISAM引擎的數(shù)據(jù)表沽翔;作用于CHAR,VARCHAT窿凤,TEXT數(shù)據(jù)的列仅偎。
4)聯(lián)合索引:將幾個(gè)列作為一條索引進(jìn)行檢索,適用最左匹配原則雳殊。

建立索引的原則

1)最左匹配原則橘沥。
這是非常,非常夯秃,非常重要的原則:MySQL使用聯(lián)合索引時(shí)座咆,會(huì)從左邊一直向右匹配,直到遇到范圍查詢(>, <, between, like 操作)就停止匹配仓洼。

比如:where a=1 and b=2 and c>3 and d=4介陶,如果建立的是(a,b,c,d)的聯(lián)合索引,那么 d 是用不到索引的色建。而如果建立成(a,b,d,c)那么d的索引就會(huì)用到了哺呜。因?yàn)镸ySQL優(yōu)化器將d=4的條件前置到了c>3前面。

MySQL創(chuàng)建聯(lián)合索引的規(guī)則是箕戳,首先會(huì)對聯(lián)合索引最左邊的字段進(jìn)行數(shù)據(jù)排序某残,在第一個(gè)字段的基礎(chǔ)上,對第二個(gè)字段進(jìn)行排序漂羊。按上面的例子來說驾锰,就相當(dāng)于實(shí)現(xiàn)了 order by a,b,c,d 的規(guī)則。

還是拿上面例子來說走越,a索引是天然有序的椭豫。當(dāng)滿足了a=1 這個(gè)條件之后,b就有序了旨指,而確定了a=1 and b=2 以后赏酥,第三個(gè)索引也是有序的了。

所以谆构,mysql索引規(guī)則中要求裸扶,要想使用聯(lián)合索引的第二個(gè)索引,必須首先使用第一個(gè)索引(而且必須是等值匹配)搬素,這也是最左匹配原則的根本原因呵晨。

2)=in 可以亂序魏保。
因?yàn)镸ySQL優(yōu)化器會(huì)判斷糾正這條SQL語句,并使用效率最高的方式利用索引摸屠,生成執(zhí)行計(jì)劃谓罗。

3)盡量選擇區(qū)分度高的列作為索引
區(qū)分度的公式是 count(distinct col) / count(*)季二,表示字段的不重復(fù)比率檩咱,結(jié)果是 [0,1] 的范圍。

比率越大胯舷,我們掃描的數(shù)據(jù)越少刻蚯,唯一索引的區(qū)分度是1。而一些狀態(tài)位桑嘶,性別字段炊汹,區(qū)分度就很低,當(dāng)數(shù)據(jù)量很大時(shí)不翩,區(qū)分度就無限趨近于0了兵扬。也就是說,即使使用了這些字段做索引口蝠,那匹配出來的數(shù)據(jù)量也會(huì)很大器钟,幾乎沒有作用。

那這個(gè)比率一般是多少呢妙蔗?根據(jù)使用場景做具體判斷傲霸,不過一般需要JOIN的字段我們要求在0.1以上,也就是平均1條數(shù)據(jù)眉反,掃描10條記錄昙啄。
</br>

4)索引不要參與計(jì)算
比如FROM_UNIXTIME(create_time) = '2016-06-06' 就不能使用索引。
原因很簡單寸五,B+樹中存儲(chǔ)的都是數(shù)據(jù)表中的字段值梳凛,但是進(jìn)行檢索時(shí),需要把所有數(shù)據(jù)進(jìn)行計(jì)算以后才能進(jìn)行比較梳杏。顯然代價(jià)太大韧拒。

5)盡可能擴(kuò)展索引,而不是新建索引
比如表中已經(jīng)有了 a 的索引十性,現(xiàn)在要使用 (a,b) 索引叛溢,那么只要將 a 索引 改為 (a,b) 索引即可,不需要新加一個(gè)索引劲适。
而且執(zhí)行SQL時(shí)楷掉,MySQL只能使用一個(gè)索引,會(huì)從多個(gè)索引中選擇一個(gè)限制最為嚴(yán)格的索引霞势。所以說烹植,索引的數(shù)量不需要太多斑鸦。



索引的優(yōu)化方法

1)索引不會(huì)包含有NULL值的列
只要列中包含有NULL值,都將不會(huì)被包含在索引中草雕,組合索引中只要有一列有NULL值鄙才,那么這一列對于此條組合索引就是無效的。所以我們在數(shù)據(jù)庫設(shè)計(jì)時(shí)促绵,不要讓索引字段的默認(rèn)值為NULL。

例如:select id from table where num is NULL 可以在num上設(shè)置默認(rèn)值0嘴纺,確保列中沒有NULL值败晴,這樣查詢可以變?yōu)椋?code>select id from table where num=0

2)索引列排序
MySQL查詢只使用一個(gè)索引,因此如果WHERE子句中已經(jīng)使用了索引的話栽渴,那么ORDER BY中的列是不會(huì)使用索引的尖坤。因此數(shù)據(jù)庫默認(rèn)排序可以符合要求的情況下,不要使用排序操作闲擦;盡量不要包含多個(gè)列的排序慢味,如果需要,最好給這些列也創(chuàng)建組合索引墅冷。

3)使用短索引
如果一個(gè)索引列纯路,只在前10~20個(gè)字符是唯一的,那么就不要堆全部字段做索引寞忿。

4)盡可能使用varchar代替char
因?yàn)関archar是變長字符串驰唬,存儲(chǔ)空間相對較小,節(jié)省存儲(chǔ)空間腔彰。對于查詢來說叫编,在一個(gè)相對較小的字符串上查詢效率也會(huì)較高。

5)盡量使用數(shù)字字段
若只含數(shù)值信息的字段不要設(shè)置為字符型霹抛,這會(huì)降低查詢和連接的性能搓逾,并會(huì)增加內(nèi)存開銷。
這是因?yàn)镸ySQL引擎在處理查詢和連接時(shí)杯拐,需要從左到右逐個(gè)比對字符串的每個(gè)字符霞篡,而對于數(shù)字,只要比較一次就夠了藕施。

6)LIKE語句操作
一般情況下寇损,不建議使用LIKE操作;如果非使用不可裳食,如何使用也是一個(gè)研究的課題矛市。
例如:LIKE "%aaaaa%"不會(huì)使用索引,但是LIKE "aaa%"卻可以使用索引诲祸。

7)不要在索引列上進(jìn)行運(yùn)算
在建立索引的原則中浊吏,提到了索引列不能進(jìn)行運(yùn)算而昨,這里就不再贅述了。



SQL 語句的優(yōu)化

1)盡量避免在 where 子句中使用 > < != 操作符找田,否則數(shù)據(jù)庫引擎將放棄索引使用全表掃描歌憨。

2)盡量避免在 where 子句中使用 or 條件,否則數(shù)據(jù)庫引擎將放棄索引進(jìn)行全表掃描墩衙。(可以使用 union 來代替or連接查詢結(jié)果)
舉栗: select id from t where num=10 or num=20 可以這樣查詢: select id from t where num=10 union all select id from t where num=20

3)慎用 innot in务嫡。對于一組連續(xù)的數(shù)據(jù),可以使用 between代替漆改⌒牧澹或者可以考慮使用 exists 代替 in

4)like %aaa% 語句會(huì)導(dǎo)致全表掃描。

5)盡量避免在 where 子句中進(jìn)行表達(dá)式操作挫剑,這將導(dǎo)致放棄索引使用全表掃描去扣。
舉栗:select id from t where num/2=100 應(yīng)改為: select id from t where num=200

6)盡量避免在 where 子句中對字段進(jìn)行函數(shù)操作,可以在 = 右邊進(jìn)行結(jié)果匹配樊破。
舉栗:select id from t where substring(name,1,3)=’abc’ 應(yīng)改為:select id from t where name like ‘a(chǎn)bc%’

7)任何地方都不要使用 select * from table 愉棱,查詢應(yīng)該指定具體的字段來代替 *,不要返回用不到的字段哲戚。

8)應(yīng)盡量避免向客戶端返回大數(shù)據(jù)量奔滑。
如果結(jié)果條數(shù)過多,考慮在業(yè)務(wù)端使用分頁請求惫恼。
如果有大字段(BLOB)档押,則最好在業(yè)務(wù)端提供剔除大字段的接口請求(例如 selectById 和 selectByIdwithBlob)。
(好吧這一條不屬于SQL優(yōu)化祈纯,但是也需要牢記)



關(guān)于數(shù)據(jù)庫表的使用優(yōu)化

1)字符串字段使用 varchar 而不要使用char令宿。

2)單表字段不要太多,建議在20個(gè)字段以內(nèi)腕窥。原因是存儲(chǔ)引擎的API在工作時(shí)需要在服務(wù)器層和存儲(chǔ)引擎層之間通過行緩沖方式拷貝數(shù)據(jù)粒没,然后在服務(wù)器層將緩沖內(nèi)容轉(zhuǎn)碼成各個(gè)列,這個(gè)轉(zhuǎn)換過程代價(jià)非常高簇爆。如果字段太多癞松,建議拆分成多個(gè)表,但注意不要過度設(shè)計(jì)入蛆。

3)在索引列上盡量設(shè)置值為 NOT NULL响蓉。因?yàn)樵谒饕猩线M(jìn)行NULL判斷將會(huì)導(dǎo)致放棄使用索引。

4)通常來講哨毁,沒必要使用 decimal 類型枫甲。即使是在存儲(chǔ)財(cái)務(wù)數(shù)據(jù)時(shí)也可以使用int 或者 bigint 類型,只要放大一定的備注就可以消除誤差。而精確計(jì)算的代價(jià)太高想幻。

5)時(shí)間使用DATETIME粱栖,不建議使用TIMESTAMP。有的地方建議使用TIMESTAMP脏毯,因?yàn)樗徽加?個(gè)字節(jié)闹究,DATETIME占用8個(gè)字節(jié)。但是TIMESTAMP只能表示1970-2038年食店。而且會(huì)因?yàn)闀r(shí)區(qū)而不同渣淤。



(如果有什么錯(cuò)誤或者建議,歡迎留言指出)
(本文內(nèi)容是對各個(gè)知識(shí)點(diǎn)的轉(zhuǎn)載整理吉嫩,用于個(gè)人技術(shù)沉淀砂代,以及大家學(xué)習(xí)交流用)


參考資料:
在一個(gè)千萬級數(shù)據(jù)庫查詢中,如何提高查詢效率
Mysql學(xué)習(xí)之索引
MySQL最左匹配原則的理解
SQL優(yōu)化-索引——掘金
SQL優(yōu)化——簡書
SQL優(yōu)化核心思想

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末率挣,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子露戒,更是在濱河造成了極大的恐慌椒功,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件智什,死亡現(xiàn)場離奇詭異动漾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)荠锭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門旱眯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人证九,你說我怎么就攤上這事删豺。” “怎么了愧怜?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵呀页,是天一觀的道長。 經(jīng)常有香客問我拥坛,道長蓬蝶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任猜惋,我火速辦了婚禮丸氛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘著摔。我一直安慰自己缓窜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著雹洗,像睡著了一般香罐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上时肿,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天庇茫,我揣著相機(jī)與錄音,去河邊找鬼螃成。 笑死旦签,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的寸宏。 我是一名探鬼主播宁炫,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼氮凝!你這毒婦竟也來了羔巢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤罩阵,失蹤者是張志新(化名)和其女友劉穎竿秆,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稿壁,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幽钢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了傅是。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匪燕。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖喧笔,靈堂內(nèi)的尸體忽然破棺而出帽驯,到底是詐尸還是另有隱情,我是刑警寧澤书闸,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布界拦,位于F島的核電站,受9級特大地震影響梗劫,放射性物質(zhì)發(fā)生泄漏享甸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一梳侨、第九天 我趴在偏房一處隱蔽的房頂上張望蛉威。 院中可真熱鬧,春花似錦走哺、人聲如沸蚯嫌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽择示。三九已至束凑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間栅盲,已是汗流浹背汪诉。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谈秫,地道東北人扒寄。 一個(gè)月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像拟烫,于是被迫代替她去往敵國和親该编。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容